Acwing 95. 费解的开关
递推,位运算
本题的限制较多,所以爆搜加上剪枝是可以过的;
这里采用枚举操作的方法,可以大大提高运行效率。
首先给你一副\(5*5\)的\(0,1\)地图,要求你用十字变换的方法使地图全部变成\(1\),并且操作数小于6次。
这里按行来看,如果你改变了第\(i\)行的\(j\)号灯,那么\(j\)号灯上下两行的\(j\)号灯都会因此改变。
而你改变了\(j\)号灯,也会使行内\(j\)号灯两边的灯改变,这会增加操作的次数。
而本题有一个性质:每个点只会被操作一次,并且操作的次序可以任意
那么我们可以设想:用\(i+1\)行来补全\(i\)行,到最后一行时判断最后一行是否都为\(1\),来判断方案是否成立。
但是大家很容易想到:那第一行呢,一定可以举出反例,证明第一行操作后也存在最佳方案。
那么我们就先枚举第一行所有操作的情况,然后一行一行向下遍历。
问题影人而解!
AC code
#include <iostream>
using namespace std;
const int N = 11;
char s[N][N];
int a[N][N],at[N][N];
int dx[]={0,0,0,1,-1},dy[]={0,1,-1,0,0};
void turn(int sx, int sy)
{
for(int i = 0; i < 5; i ++ )
{
int x = sx + dx[i], y = sy + dy[i];
if(x < 0 || x > 4 || y < 0 || y > 4) continue;
a[x][y] ^= 1;
}
}
int main()
{
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
int T; cin >> T;
while(T -- )
{
for(int i = 0; i < 5; i ++ ) cin >> s[i];
for(int i = 0; i < 5; i ++ )
for(int j = 0; j < 5; j ++ )
at[i][j] = a[i][j] = s[i][j] - '0';
int res = 10;
for(int op = 0; op < 1 << 5; op ++ )
{
int cnt = 0;
for(int k = 0; k < 5; k ++ )
if(op >> k & 1) turn(0, k),cnt ++ ;
for(int i = 0; i < 4; i ++ )
for(int j = 0; j < 5; j ++ )
{
if(cnt > 6) break;
if(!a[i][j]) turn(i + 1, j), cnt++;
}
bool flag = true;
for(int i = 0; i < 5; i ++ )
if(!a[4][i]) flag = false;
if(flag) res = min(res, cnt);
memcpy(a, at, sizeof at);
}
if(res <= 6) cout << res << endl;
else puts("-1");
}
return 0;
}
本文来自博客园,作者:{三季野花},转载请注明原文链接:https://www.cnblogs.com/SanGarden/articles/17063704.html