来自《算法竞赛进阶指南》(李煜东)题库
http://contest-hunter.org:83/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
####描述
你玩过“拉灯”游戏吗?25盏灯排成一个5x5的方形。每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。
我们用数字“1”表示一盏开着的灯,用数字“0”表示关着的灯。下面这种状态
10111
01101
10111
10000
11011
在改变了最左上角的灯的状态后将变成:
01111
11101
10111
10000
11011
再改变它正中间的灯后状态将变成:
01111
11001
11001
10100
11011
给定一些游戏的初始状态,编写程序判断游戏者是否可能在6步以内使所有的灯都变亮。
####输入格式
第一行有一个正整数n,代表数据中共有n个待解决的游戏初始状态。
以下若干行数据分为n组,每组数据有5行,每行5个字符。每组数据描述了一个游戏的初始状态。各组数据间用一个空行分隔。
对于30%的数据,n<=5;
对于100%的数据,n<=500。
####输出格式
输出数据一共有n行,每行有一个小于等于6的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。
对于某一个游戏初始状态,若6步以内无法使所有灯变亮,请输出“-1”。
####样例输入
3
00111
01011
10001
11010
11100
11101
11101
11110
11111
11111
01111
11111
11111
11111
11111
####样例输出
3
2
-1
####来源
Matrix67
####思路
穷举法,但实际上只要列举32种情况就行了。按行的顺序从上到下按开关,对第一行共2^5情况穷举,剩下的所有行的情况都受限于当前状态下前一行的状态,因为首先每个开关只按一次(多出来的其实没用,奇数次等价于1次,偶数次等于没按),其次当某一行按完了,它没亮的灯只能托付给下一行来打开。这说明在前一行状态固定时,当前行的开关策略是确定的。可以考虑使用为运算,用一个int存一行的状态,但注意的是只有低5位才有效,其他应该置零。
####代码
#include
#include
#include
#include
using namespace std;
const int N = 5;
const int ALL_ONE = (1 << N) - 1;
const int INF = 0x3f3f3f3f;
int a[N], b[N];
bool is_ALL_ONE(int number)
{
return ALL_ONE == (ALL_ONE & number);
}
void copy_(int from_[], int to_[])
{
for(int i = 0; i < N; i++)
to_[i] = from_[i];
}
int count_one(int number)
{
int cnt = 0;
for(; number; number >>= 1)
cnt += (1 & number);
return cnt;
}
void switch_(int lights[], int cur_i, int switch_state)
{
if(cur_i - 1 >= 0)
lights[cur_i - 1] = ALL_ONE & (switch_state ^ lights[cur_i - 1]);
if(cur_i + 1 < N)
lights[cur_i + 1] = ALL_ONE & (switch_state ^ lights[cur_i + 1]);
for(int i = 0; i < N; i++)
{
if(1 & (switch_state >> i))
{
int ss = (1 << i + 1) | (1 << i) | (1 << i - 1);
lights[cur_i] = ALL_ONE & (ss ^ lights[cur_i]);
}
}
}
void switch_a(int cur_i, int switch_state)
{
switch_(a, cur_i, switch_state);
}
int enumerate_()
{
int ans = INF;
for(int s = 0; s <= ALL_ONE; s++)
{
int ss = s, cnt = 0;
for(int i = 0; i < N; i++)
{
cnt += count_one(ss);
switch_a(i, ss);
ss = (~a[i])&ALL_ONE;
}
if(is_ALL_ONE(a[N - 1]))
{
if(cnt < ans)
{
ans = cnt;
}
}
copy_(b, a);
}
if(ans > 6)
return -1;
else
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
cout << "no ONLINE_JUDGE" << endl;
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif // ONLINE_JUDGE
int n;
cin >> n;
while(n--)
{
memset(a, 0, sizeof(a));
for(int i = 0; i < N; i++)
{
string tmp;
cin >> tmp;
for(int j = 0; j < N; j++)
{
a[i] <<= 1;
a[i] |= tmp[j] - '0';
}
}
copy_(a, b);
cout << enumerate_() << endl;
}
return 0;
}