http://poj.org/problem?id=3279
状态压缩+bfs
先说说思路吧:一个点的翻转对其上下有影响,故i+1行翻转结束后,必须确保i-th全部为零,故采取递推的方式,第一行的翻转状态任意,地推到第二行,第二行的操作可以被唯一确定因为该行的操作必须保证上一行全部为零,以此类推到末行,末行的操作保证上一行为零且自己也为零。
细节处理:以下bfs代码如果改成传统的bfs形式(即层层调用函数会超时),开始一直超时改成循环递推才过的。对自己特别强调下各个位运算的优先度,其他的一些见代码中的注释。
ac code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<climits>
#define handle(row,i) temp[t][row]^=i,temp[t][row]^=i<<1,temp[t][row]^=i>>1
// 通过位运算实现 翻转操作
using namespace std;
const int M=INT_MAX/2;
int n,m;
int temp[2][17],tt[17],re[17];
int record[1<<15],num;
void init()
{
int t;
memset(temp,0,sizeof(temp));
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
scanf("%d",&t);
if(t==1)temp[0][i]+=1<<(m-j);
//将地图转换成二进制数便于位操作
}
}
}
void pre()
{
int t;
memset(record,0,sizeof(record));
for(int i=0;i<1<<15;++i)
{
for(int j=0;j<15;++j)
{
t=1<<j;
if((i&t))record[i]++;//记下对一整行某种操作的代价
}
}
}
bool cmp()
{
for(int i=1;i<=n;++i)
{
if(tt[i]<re[i])return true;
else if(tt[i]>re[i])return false;//保证题目要求的字典序
}
}
int main()
{
pre();
while(cin>>n>>m)
{
init();
num=1<<m;
int t=1,hand,sum,ans;
memset(re,0,sizeof(re));
memset(tt,0,sizeof(tt));
ans=M;
for(int i=0;i<num;++i)
{
sum=0;
for(int j=1;j<=n;++j)
temp[t][j]=temp[!t][j];
handle(1,i);
if(temp[t][1]&num)temp[t][1]^=num;
temp[t][2]^=i;
sum+=record[i];
tt[1]=i;
for(int k=2;k<=n;++k)
{
int hand=0;
for(int j=0;j<m;++j)
{
if(temp[t][k-1]&1<<j)hand+=1<<j;
}
handle(k,hand);
if(temp[t][k]&num)temp[t][k]^=num;
temp[t][k+1]^=hand;
sum+=record[hand];
tt[k]=hand;
}
if(temp[t][n]==0&&(ans>sum||ans==sum&&cmp()))
{
for(int j=1;j<=n;++j)
re[j]=tt[j];
ans=sum;
}
}
if(ans==M)puts("IMPOSSIBLE");
else
{
for(int i=1;i<=n;++i)
{
for(int j=m-1;j>=0;--j)
{
int t;
t=((re[i]&1<<j)==1<<j);//‘&’的优先度比‘==’低,但‘<<’的优先度比‘==’高
cout<<t<<(j>0?' ':'\n');
}
}
}
}
}