2020年牛客算法入门课练习赛3 A B E

2020年牛客算法入门课练习赛3

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,a1] 可以被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);
	}
}

你可能感兴趣的:(BFS,DFS,数论)