费解的开关
joyoi-tyvj 1266
题目大意:
有5*5的一个图,每个点的数值是1或0,如果将一个点的数值取反,那这个点上下左右的点都会取反,现在问你将所有点都变为1最少要多少步,如果步数大于6或无法全变成1的话就输出-1(有n组数据)
输入样例
3
00111
01011
10001
11010
1110011101
11101
11110
11111
1111101111
11111
11111
11111
11111
输出样例
3
2
-1
解题思路:
首先我们可以发现一个点取反两次和没取反是一样的,所以我们可以暴力枚举25个点变不变,但这样就要o(22525n)o(2^{25}25n)o(22525n),就绝对会炸
然后我们一行一行地来看,可以发现如果当前行处理后,当前行0的位置下方必须点,1的位置下方不能点,然后我们就可以只枚举第一行然后剩下的直接算出来,最后直接判断最后一行合不合法即可,时间复杂度o(2525n)o(2^{5}25n)o(2525n)
代码:
#include<cstdio>
#define min(a,b) (a)<(b)?(a):(b)
using namespace std;
int n,ans,a[7][7],b[7][7];
int js()
{int sum=0;for (int i=1;i<=5;++i)for (int j=1;j<=5;++j)b[i][j]=a[i][j];//用b来临时计算for (int i=1;i<=4;++i)for (int j=1;j<=5;++j)if (!b[i][j])//如果是0就下方的数取反{b[i][j]^=1;b[i+1][j]^=1;b[i+2][j]^=1;b[i+1][j-1]^=1;b[i+1][j+1]^=1;sum++;}for (int i=1;i<=5;++i)if (!b[5][i])//不合法return 10;//超过6就可以了return sum;
}
void dfs(int dep,int x)
{if (dep>5){ans=min(ans,x+js());//求最小值return;}a[1][dep]^=1;//取反a[1][dep-1]^=1;a[1][dep+1]^=1;a[2][dep]^=1;dfs(dep+1,x+1);a[1][dep]^=1;//不取反a[1][dep-1]^=1;a[1][dep+1]^=1;a[2][dep]^=1;dfs(dep+1,x);return;
}
int main()
{scanf("%d",&n);for (int t=1;t<=n;++t){for (int i=1;i<=5;++i)for (int j=1;j<=5;++j)scanf("%1d",&a[i][j]);//输入一位数ans=10;dfs(1,0);printf("%d\n",ans<7?ans:(-1));}return 0;
}