A 胖胖的牛牛
思路:BFS。要求转弯次数最小值,那么就需要知道走下一步是否存在转弯的情况,所以在结构体中需要加上到达这个点是怎么过来的标记。并且需要优先考虑转弯次数较小的点的情况,使用优先队列搞一下。大概做法就是这样,但是仔细考虑一下vis数组好像不能只开二维的了,同一个点又可能会被不同的路线走上很多次,那么只需要把vis数组再加一维就可以解决这个问题,因为每个点的每个方向只可能有效的被经过一次。大概思路就是这样了。
#include
using namespace std;
int n, stai, staj, endi, endj;
int dirx[] = {0, 1, 0, -1};
int diry[] = {1, 0, -1, 0};
bool vis[105][105][5];
char g[105][105];
struct node {
int x, y;
int pre, cnt;
node(int i, int j, int p, int c) {x=i;y=j;pre=p;cnt=c;}
bool operator < (const node& a) const{
return cnt > a.cnt;
}
};
priority_queue <node> q;
int bfs() {
q.push(node(stai, staj, 4, 0));
while(!q.empty()) {
node t = q.top(); q.pop();
if(vis[t.x][t.y][t.pre]) continue;
vis[t.x][t.y][t.pre] = 1;
if(t.x == endi && t.y == endj) return t.cnt;
for(int i = 0;i < 4; i++) {
int tx = t.x+dirx[i], ty = t.y+diry[i], k;
if(tx<1||tx>n||ty<1||ty>n||g[tx][ty]=='x') continue;
if(t.pre == i || t.pre == 4) k = 0;
else k = 1;
q.push(node(tx, ty, i, t.cnt+k));
}
}
return -1;
}
int main() {
scanf("%d", &n);
for(int i = 1;i <= n; i++) {
for(int j = 1;j <= n; j++) {
scanf(" %c", &g[i][j]);
if(g[i][j] == 'A') stai = i, staj = j;
if(g[i][j] == 'B') endi = i, endj = j;
}
}
printf("%d\n", bfs());
}
/*
4
x x A .
x . . .
. . x .
B . . .
*/
B 牛牛的零食
思路:首先想到的应该是把 [ a , b ] [a,b] [a,b] 之间能被8整除的数的个数求出来,然后减去那些既能够被8整除也能够被输入的数 以及这些数组合起来的数 整除的数 的个数,组合的意思就是这些数的最小公倍数。
比如需要找到 [ 1 , 30 ] [1,30] [1,30] 能被2整除 但不能被 3、5整除的数的个数。
我们可以知道这个区间里面有15个数可以被2整除,现在我们需要从这15个数中减去也能被3、5整除的数。
先看3 : { 6,12,18,24,30 } 是要被减去的
再看5: {10,20,30} 是要被减去的
然后用15-5-3 = 7就是答案了么? 当然不是,不难发现30被减去了两次,需要再把答案+1
容斥原理就是这样的,所以也有一个口诀,奇加偶减;意思就是集合为奇数的时候对答案贡献为加,集合个数为偶数的时候对答案贡献为减。上面的例子也是这样,只算区间范围内可以被2整除的时候对答案贡献为加,然后需要减去lcm(2,3)和lcm(2,5)的个数,这时候又需要加上lcm(2,3,5)的个数。
所以这道题求出 [ 1 , b ] [1,b] [1,b] 与 [ 1 , a − 1 ] [1,a-1] [1,a−1] 可以被8整除但不能被其他数整除的数的个数再做减法就可以了。然后用dfs实现对于其他数的lcm的组合,用一个标记作出是奇数还是偶数之间的lcm,递归到底的时候对答案进行更新就好了。
#include
using namespace std;
int n, a, b, x[20], ans;
long long gcd(long long a, long long b) {
if(b == 0) return a;
else return gcd(b, a%b);
}
void dfs(int t, int pos, long long di, bool ok) {
if(pos == n+1) {
if(ok) ans -= t/di;
else ans += t/di;
return;
}
dfs(t, pos+1, di, ok);
dfs(t, pos+1, di*x[pos]/gcd(di,x[pos]), ok^1);
}
int main() {
scanf("%d", &n);
for(int i = 1;i <= n; i++) {
scanf("%d", &x[i]);
}
scanf("%d %d", &a, &b);
dfs(a-1,1,8,0); ans = -ans;
dfs(b,1,8,0);
printf("%d\n", ans);
}
E 只能吃土豆的牛牛
思路:
k | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
k的二进制 | 1 | 10 | 11 | 100 | 101 | 110 | 111 | 1000 | 1001 |
总重量 | 3 0 3^0 30 | 3 1 3^1 31 | 3 1 + 3 0 3^1+3^0 31+30 | 3 2 3^2 32 | 3 2 + 3 0 3^2+3^0 32+30 | 3 2 + 3 1 3^2+3^1 32+31 | 3 2 + 3 1 + 3 0 3^2+3^1+3^0 32+31+30 | 3 3 3^3 33 | 3 3 + 3 1 3^3+3^1 33+31 |
这样就很清晰了,可以写了。
#include
using namespace std;
int _, k, cas;
long long pre[40];
bool vis[40];
int main() {
pre[0] = 1;
for(int i = 1;i <= 32; i++) {
pre[i] = pre[i-1]*3;
}
scanf("%d", &_);
while(_--) {
memset(vis, 0, sizeof(vis));
scanf("%d", &k);
int cnt = 0;
while(k) {
if(k&1) vis[cnt] = 1;
cnt++;
k >>= 1;
}
long long ans = 0;
for(int i = 0;i <= 32; i++) {
if(vis[i]) ans += pre[i];
}
printf("Case #%d: ", ++cas);
printf("%lld\n", ans);
}
}