既然自己那么水,看书的时候遇到不会的就先跳过吧。
今天再次见到队友直接变成了一条狗。
题意:
给你一个n*n的01矩阵,要求把尽量少的0变成1,使得每个元素的上下左右元素(如果存在的话)之和均为偶数。
Input:
第一行为数据组数t。每组数据第一行为正整数n(1<=n<=15);接下来n行包含n个非0即1的整数,相邻整数之间用一个空格隔开。
Output:
对于每组数据,输出被改变的元素的最小个数,无解则输出-1。
朴素的想法就是枚举每一个点是否从0变成1,但15*15个元素深搜枚举要2^15,太大,考虑回溯法。
从左上往右下遍历的话,显然右下的元素会受到左上元素的影响,那么如果第一行已经确定了,易证下面的元素都是确定的,因此只需要枚举第一行的所有情况,然后就可以在多项式时间内算出整个矩阵或者是求出这种情况不可能。
用dfs实现,先遍历第一行的情况,再算出矩阵,边算边判断。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
#define mxn 20
int a[mxn][mxn];
int n;
bool flag[mxn][mxn];
void change(int i,int j){
if(i) flag[i-1][j]=1-flag[i-1][j];
if(j) flag[i][j-1]=1-flag[i][j-1];
if(i!=n-1) flag[i+1][j]=1-flag[i+1][j];
if(j!=n-1) flag[i][j+1]=1-flag[i][j+1];
}
int dfs(int x,int ans){
if(x==n){
//cout<<"hehe "<<endl;
//for(int i=0;i<n;++i)
// cout<<a[0][i]<<" ";
//cout<<endl;
memset(flag,0,sizeof(flag));
for(int i=0;i<n;++i)
for(int j=0;j<n;++j){
int tem=0;
if(j!=n-1) tem+=a[i][j+1];
if(i!=n-1) tem+=a[i+1][j];
if(i) tem+=a[i-1][j];
if(j) tem+=a[i][j-1];
flag[i][j]=tem%2;
}
for(int i=0;i<n-1;++i)
for(int j=0;j<n;++j){
if(!flag[i][j]) continue;
if(!a[i+1][j]){ change(i+1,j);++ans; }
else return -1;
}
for(int i=0;i<n;++i)
if(flag[n-1][i]) return -1;
//cout<<"ans "<<ans<<endl;
return ans;
}
int ret=dfs(x+1,ans);
if(a[0][x]==1) return ret;
a[0][x]=1-a[0][x];
//cout<<"x: "<<x<<endl;
int tem=dfs(x+1,ans+1);
if(ret==-1) ret=tem;
else ret=(tem==-1)?ret:min(tem,ret);
a[0][x]=1-a[0][x];
return ret;
}
int main(){
int cs;
scanf("%d",&cs);
for(int CS=1;CS<=cs;++CS){
scanf("%d",&n);
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)
scanf("%d",&a[i][j]);
printf("Case %d: ",CS);
printf("%d\n",dfs(0,0));
}
return 0;
}