题目概述:
给你一个N*N的矩阵,元素为0或1。问你最少把几个0变成1能使每一个元素上下左右(存在哪个加哪个)元素加起来是偶数。如果做不到输出-1;
思路:
N是<=15的。所以可以枚举第一行。然后可以通过第一行来确定剩下的。思路不难,有一个小技巧。利用二进制,巧妙地枚举了所有情况。
比如,n=3.那么,需要枚举2^3=8种情况。分别为:0 0 0,0 0 1,0 1 0,0 1 1 ,1 0 0 ,1 0 1 ,1 1 0,1 1 1.可以发现,恰好是0~7这八个数字的二进制。
然后设置循环变量s,s从0到7.
在设置第i个位置是0还是1的时候,用2^i和s进行&运算,是0就是0,非0就是1。
效果为:
s=0: s=1: s=2: s=3: s=4: s=5: s=6: s=7:
i=0:0&1=0 i=0:1&1=1 i=0:2&1=0 i=0:3&1=1 i=0:4&1=0 i=0:5&1=1 i=0:6&1=0 i=0:7&1=1
i=1:0&2=0 i=1:1&2=0 i=1:2&2=2 i=1:3&2=2 i=1:4&2=0 i=1:5&2=0 i=1:6&2=2 i=1:7&2=2
i=2:0&4=0 i=2:1&4=0 i=2:2&4=0 i=2:3&4=0 i=2:4&4=4 i=2:5&4=4 i=2:6&4=4 i=2:7&4=4
0 0 0 1 0 0 0 1 0 1 1 0 0 0 1 1 0 1 0 1 1 1 1 1
由上表可知其原理。
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=15+5;
const int INF=1000000000;
int n;
int before[maxn][maxn],after[maxn][maxn];
int check(int s)
{
int i;
for (i=0;i<n;i++)
{
memset(after,0,sizeof(after));
for (i=0;i<n;i++)
{
if ((1<<i)&s) after[0][i]=1;
else
if (before[0][i] == 1) return INF;
}
int c,r;
for (r=1;r<n;r++)
{
for (c=0;c<n;c++)
{
int sum=0;
if (r>1) sum+=after[r-2][c];
if (c>0) sum+=after[r-1][c-1];
if (c<n-1) sum+=after[r-1][c+1];
after[r][c]=sum%2;
if (before[r][c] == 1 && after[r][c] == 0) return INF;
}
}
int count=0;
for (r=0;r<n;++r)
for (c=0;c<n;++c)
if (before[r][c] != after[r][c]) count++;
return count;
}
}
int main()
{
int kaseNum,kase;
scanf("%d",&kaseNum);
for (kase=0;kase<kaseNum;++kase)
{
scanf("%d",&n);
int i,j;
for (i=0;i<n;++i)
for (j=0;j<n;++j)
scanf("%d",&before[i][j]);
int ans=INF;
for (i=0;i<(1<<n);++i)
{
ans=min(ans,check(i));
}
if (ans == INF) ans=-1;
printf("Case %d: %d\n",kase+1,ans);
}
return 0;
}