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;
}
posted @ 2023-01-21 12:17  Sankano  阅读(23)  评论(0编辑  收藏  举报