做过很多遍的二进制搜索,每次写就是对着代码敲一遍,然后下次写又不会
链接:http://poj.org/problem?id=3279
题意:n*m的矩阵,全是0和1构成的,当选择翻一个点的时候,周围的点会受到影响,问选择翻n*m矩阵中的哪些点,可以将所有点均翻过来?
为什么要用搜索?因为n和m很小,最大是15
为什么要用二进制搜索?因为是枚举状态,而且是每行每列的状态
怎么样枚举最好?枚举第一行,然后依次遍历其他行,枚举第一行的复杂度是2的n次方,然后遍历全矩阵式n*m,整个复杂度相乘没有问题
怎么搜索呢?
用一个整数表示1行的状态,比如255=11111111(2),那么循环就是从0到255表示256种情况,255的第0位至第7位均是1,所以呢,表示全部需要翻,然后从第二行到第n-1行依次推理得需要或者不需要翻,再用最后一行来检验方案是不是可行
const int maxn=20;
const int inf=0x3f3f3f3f;
int n,m;
int mp[maxn][maxn];
int mem[maxn][maxn];
int ans[maxn][maxn];
int mx[]={0,0,1,-1};
int my[]={1,-1,0,0};
bool ok(int x,int y){
if (x<0||y<0||x>=m||y>=n) return false;
return true;
}
bool calc(int x,int y){
int ret=mp[x][y]+mem[x][y];
for(int i=0;i<4;i++){
int tx=x+mx[i];
int ty=y+my[i];
if (ok(tx,ty)) ret+=mem[tx][ty];
}
return ret%2;
}
void nxt(int x,int y,int &tx,int &ty){
tx=x;
ty=y+1;
if (ty==n){
++tx;
ty=0;
}
}
int dfs(int x,int y,int num){
if (x==m) return num;
bool tem=calc(x-1,y);
if (tem){
mem[x][y]=true;
++num;
}
int tx,ty;
nxt(x,y,tx,ty);
return dfs(tx,ty,num);
}
int search(int in){
memset(mem,false,sizeof(mem));
int ret=0;
for(int i=0;i<n;i++)
if ((in>>i)&1){
mem[0][i]=true;
++ret;
}
ret+=dfs(1,0,0);
for(int i=0;i<n;i++)
if (calc(m-1,i)) return inf;
return ret;
}
int main(){
//freopen("input.txt","r",stdin);
while(scanf("%d%d",&m,&n)!=EOF){
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
scanf("%d",&mp[i][j]);
int tem=inf;
for(int i=0;i<(1<<n);i++){
int rec=search(i);
if (tem>rec){
tem=rec;
memcpy(ans,mem,sizeof(mem));
}
}
if (tem==inf){
puts("IMPOSSIBLE");
continue;
}
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
printf("%d%c",ans[i][j],j==n-1?'\n':' ');
}
return 0;
}