P1985 [USACO07OPEN]翻转棋 Fliptile S
和95. 费解的开关同题。
暴力总共有\(2^{NM}\)种翻转的方法。
先确定第一行的翻转方式,然后可以很容易判断这样是否存在解以及解的最小步数是多少,最上面一行的翻转方式共有\(O(2^M)\)种,复杂度为\(O(NM2^M)\)。
const int N=20;
bool g[N][N],tmp[N][N];
int flip[N][N],ans[N][N];
int n,m;
bool check(int x,int y)
{
return x>=0 && x<n && y>=0 && y<m;
}
void change(int x,int y)
{
flip[x][y]=1;
for(int i=0;i<5;i++)
{
int a=x+dx[i],b=y+dy[i];
if(check(a,b))
tmp[a][b]^=1;
}
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>g[i][j];
int res=INF;
for(int s=0;s<(1<<m);s++)
{
memcpy(tmp,g,sizeof g);
memset(flip,0,sizeof flip);
int cnt=0;
for(int i=0;i<m;i++)
if(s>>i & 1)
change(0,i),cnt++;
for(int i=0;i<n-1;i++)
for(int j=0;j<m;j++)
if(tmp[i][j])
change(i+1,j),cnt++;
bool ok=true;
for(int i=0;i<m;i++)
if(tmp[n-1][i])
{
ok=false;
break;
}
if(ok && cnt < res)
{
res=cnt;
memcpy(ans,flip,sizeof flip);
}
}
if(res == INF) puts("IMPOSSIBLE");
else
{
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
cout<<ans[i][j]<<' ';
cout<<endl;
}
}
//system("pause");
return 0;
}