解题思路:将第一行全部情况进行枚举(注意只对0转1,不能1转0),然后对于第一行的每种情况确定下面所有行的情况,并进行判断当前矩阵是否满足条件,求出最小的值
注意事项:这种题不需要对所有元素枚举,只需要枚举第一行即可,后面几行可以由第一行决定
注意全局变量的使用,枚举第一行可以使用输入矩阵,但是后面后几行的改变时一定不可以使用输入矩阵直接更改
注意只可以对0转1,不可以对1转0
子集枚举的方法
#include <stdio.h>
#include <iostream>
using namespace std;
#define maxn 20//
int n;
int size=1000000000;//最大数
int tmp=0;
bool a[maxn][maxn];
bool tmp3[maxn][maxn];
int position[4][2]={{1,0},{0,1},{-1,0},{0,-1}};//四个方向
bool range(int i,int j){//范围
if(i<n&&i>=0&&j<n&&j>=0){
return true;
}
return false;
}
void test(){//判断当前的第一行的情况可以成立
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
tmp3[i][j]=a[i][j];
}
}
int tmp2=tmp;
for(int i=0;i<n-1;i++){
for(int j=0;j<n;j++){
int c=0;
for(int k=0;k<4;k++){
if(range(i+position[k][0],j+position[k][1])){//统计附近1的个数
if(tmp3[i+position[k][0]][j+position[k][1]]){
c++;
}
}
}
if(c%2!=0){
if(tmp3[i+1][j]){//假如当前元素1的个数为奇数但是下一行为1时说明这种情况不成立
return;
}
tmp3[i+1][j]=true;//将0转1
tmp2++;
}
}
}
for(int i=0;i<n;i++){//最后一行判断是否为偶数
int c=0;
for(int k=0;k<4;k++){
if(range(n-1+position[k][0],i+position[k][1])){
if(tmp3[n-1+position[k][0]][i+position[k][1]]){
c++;
}
}
}
if(c%2!=0){//说明这种情况不行
return;
}
}
size=min(size,tmp2);
}
void first(int x){//遍历第一行全部情况
if(x==n){//判断
test();
}else{
if(!a[0][x]){//只有当元素为0才能转1
a[0][x]=true;
tmp++;
first(x+1);
a[0][x]=false;
tmp--;
}{
first(x+1);//当前元素为1时不做任何动作
}
}
}
int main(){//
int m;
cin>>m;
for(int k=0;k<m;k++){
size=1000000000;
tmp=0;
cin>>n;
int b;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>b;
if(b==1)a[i][j]=true;
else a[i][j]=false;
}
}
first(0);
cout<<"Case "<<k+1<<": ";
if(size!=1000000000)
cout<<size<<endl;
else cout<<-1<<endl;//当前所有情况都不行
}
}