CH0201 费解的开关 枚举

题目链接

http://noi-test.zzstep.com/contest/0x00%E3%80%8C%E5%9F%BA%E6%9C%AC%E7%AE%97%E6%B3%95%E3%80%8D%E4%BE%8B%E9%A2%98/0201%20%E8%B4%B9%E8%A7%A3%E7%9A%84%E5%BC%80%E5%85%B3

分析

容易想到,对同一位置按两次相当于没按,因此每个位置最多被按一次;另外,一旦我们固定了第一行的按法,那么为使得最终状态全为 1 1 1,第二行的按法也是固定的。因此,枚举第一行的按法共 32 32 32 种,之后每一行递推即可。

AC代码

#include 
#include 
#include 

using namespace std;

inline int read() {
	int num = 0;
	char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	while (c >= '0' && c <= '9')
		num = num * 10 + c - '0', c = getchar();
	return num;
}

const int maxn = 10;

int light0[maxn], light[maxn], cnt[1 << 5];
char s[maxn];

inline void change(int x, int y) {
	if (x > 0) light[x - 1] ^= 1 << y;
	if (x < 4) light[x + 1] ^= 1 << y;
	if (y > 0) light[x] ^= 1 << (y - 1);
	if (y < 4) light[x] ^= 1 << (y + 1);
	light[x] ^= 1 << y;
}

int main() {
	for (int i = 1; i < (1 << 5); ++i) cnt[i] = cnt[i - (i & -i)] + 1;
	int n = read();
	while (n--) {
		memset(light0, 0, sizeof(light0));
		int ans = 7;
		for (int i = 0; i < 5; ++i) {
			scanf("%s", s);
			for (int j = 0; j < 5; ++j)
				if (s[4 - j] - '0') light0[i] |= 1 << j;
		}
		for (int i = 0; i < (1 << 5); ++i) {
			memcpy(light, light0, sizeof(light));
			int now = cnt[i];
			for (int j = 0; j < 5; ++j)
				if ((i >> j) & 1) change(0, j);
			for (int j = 0; j < 4; ++j)
				for (int k = 0; k < 5; ++k)
					if (!((light[j] >> k) & 1)) change(j + 1, k), ++now;
			if (light[4] == (1 << 5) - 1) ans = min(ans, now);
		}
		if (ans <= 6) printf("%d\n", ans);
		else printf("-1\n");
	}
	return 0;
}

你可能感兴趣的:(《算法竞赛进阶指南》)