恩...与白书上的思路一样
枚举第0行翻转的所有情况 然后依次向下更新
如果上一行为1,这一行肯定需要翻转,直到更新完(m-2)行
再检索一遍(m-1)行 如果(m-1)行都为0 这种情况符合题意 更新翻转的最少次数 记录下最优解
话不多说 上代码
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<stack>
#include<queue>
#include<cmath>
#include<stack>
#include<list>
#include<map>
typedef long long ll;
using namespace std;
int m,n;
int tile[17][17],most[17][17],flip[17][17]; //tile是初始地图 most记录最优解 flip记录中间结果
int nextx[5]={0,1,0,-1,0};
int nexty[5]={1,0,-1,0,0}; //翻转一个格子,其实要翻转五个格子
int get(int x,int y)
{
int c=tile[x][y]; // 初始时 坐标为(x,y)的格子的状态
for(int k=0;k<5;k++)
{
int tx=x+nextx[k],ty=y+nexty[k]; //更新到邻接格子
if(tx>=0&&tx<m&&ty>=0&&ty<n)
{
c+=flip[tx][ty]; //在初始状态的基础上加上翻转次数
}
}
return c%2;//如果为奇数 那么这个格子现在状态为1 也就是黑色
}
int cal()
{
int i,k,j;
for(i=1;i<m;i++)
{
for(j=0;j<n;j++)
{
if(get(i-1,j)!=0) //上一行格子是1,说明这一行需要翻转
{
flip[i][j]=1; //这个地方不要把邻接的格子都更新,在get函数里已经更新了!
}
}
}
for(j=0;j<n;j++)
{
if(get(m-1,j)!=0)return -1; //检索最后一行 是否全部为0
}
int res=0;
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
res+=flip[i][j]; //计算出总的翻转次数
}
}
return res;
}
int main()
{
int i,j,k;
while(scanf("%d%d",&m,&n)==2)
{
int cnt=0;
for(i=0;i<m;i++)
for(j=0;j<n;j++)
{
scanf("%d",&tile[i][j]);
cnt+=tile[i][j];
}
if(cnt==0)
{
printf("0\n"); //全部为0 无需翻转
continue;
}
int ans=0xfffffff;
for(i=0;i< 1<<n;i++)
{
memset(flip,0,sizeof(flip));//不要忘记初始化,在这个地方WA了几次...开始时翻转次数都为0
for(j=0;j<n;j++)
{
flip[0][n-j-1]=i>>j&1; //枚举第一行的翻转情况
}
int num=cal();
if(num>=0)
{
if(ans>num)
{
ans=num;
memcpy(most,flip,sizeof(flip));//更新res ,并保存最优解
}
}
}
if(ans==0xfffffff)
{
printf("IMPOSSIBLE\n");
}
else{
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
printf("%d%c",most[i][j],j+1==n?'\n':' ');
}
}
}
}
return 0;
}