CH0201费解的开关
思路
本题可以直接用bfs去做,时间复杂度较高,本文来说说递推的做法。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5;
const int INF = 10000000;
const int dx[5] = {0,1,0,-1,0};
const int dy[5] = {0,0,1,0,-1};
int t;
char g[N][N];
void turn(int x,int y)
{
for(int i = 0;i < 5;i ++ )
{
int a = x + dx[i];
int b = y + dy[i];
if(a < 5 && a >= 0 && b < 5 && b >= 0)
{
g[a][b] ^= 1;
}
}
}
int work()
{
int ans = INF;
for(int i = 0;i < 1 << 5;i ++ )
{
int res = 0;
char backup[N][N];
memcpy(backup,g,sizeof g);
for(int j = 0;j < 5;j ++ )
{
if(i >> j & 1)
{
res ++ ;
turn(0,j);
}
}
for(int i = 0;i < 4;i ++ )
{
for(int j = 0;j < 5;j ++ )
{
if(g[i][j] == '0')
{
res ++ ;
turn(i + 1,j);
}
}
}
int flag = true;
for(int i = 0;i < 5;i ++ )
{
if(g[4][i] == '0')
{
flag = false;
break;
}
}
if(flag) ans = min(ans,res);
memcpy(g,backup,sizeof g);
}
if(ans > 6) ans = -1;
return ans;
}
int main()
{
cin >> t;
while(t -- )
{
for(int i = 0;i < 5;i ++ ) cin >> g[i];
cout << work() << endl;
}
return 0;
}
三个性质:
- 每个位置至多会被点击一次。
- 若固定了第一行,则满足题意的点击方案至多只有一种。其原因是:当第\(i\)行某一位为\(1\)时,若前\(i\)行已被固定,则只能点击\(i + 1\)行使第\(i\)行改变。
- 点击的先后顺序不影响结果。
我们去枚举第一行的所有可能情况,并不改变第一行,只按第二行的开关。由性质\(2\)可得,若到达第\(n\)行不全为\(0\),说明第一行的这种枚举情况是错误的。若到达第\(n\)行全为\(0\),则说明第一行的枚举情况是正确的,更新一下答案即可。
流萤断续光,一明一灭一尺间