http://poj.org/problem?id=3279
只需要枚举第一行的2^n次方的踩踏方式
之后遍历全图 ,如果b[i-1][j] 没有被反转
那么点(i,j) 则需要反转。
以此搜索出所有可能性 然后选择最小的。
全排列按照字典序从小到大开始排列
代码总体分为三个函数
全排列函数寻找首行全排列
遍历函数计算
反转函数反转相邻六个点。
总体纯暴力
#include <iostream>
#include <cstring>
using namespace std;
#define mo 19
int a[mo][mo];//原图
int tag[mo][mo]; //记录图
int mtag[mo][mo];//记录最小图
int b[mo][mo];//复制图
int f[mo];//首行记录
int n,m;
int fuzhi()//复制原图
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
b[i][j]=a[i][j];
}
int fan(int x,int y,int &ans)//翻转函数
{
ans++;
tag[x][y]=1;
for(int i=-1;i<=1;i++)
for(int j=-1;j<=1;j++)
if(i==0||j==0)
b[x+i][y+j]=!b[x+i][y+j];
}
int mins;
int ces;
void run()//反转模拟
{
fuzhi();
memset(tag,0,sizeof(tag));
int ans=0;
for(int i=1;i<=m;i++)
if(f[i]) fan(1,i,ans);
for(int i=2;i<=n;i++)
for(int j=1;j<=m;j++)
if(b[i-1][j])
fan(i,j,ans);
int z=1;
for(int i=1;i<=m;i++)
if(b[n][i]) z=0;
if(z&&ans<mins)
{
ces=1;
mins=ans;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
mtag[i][j]=tag[i][j];
}
}
void dfs(int t,int x)//全排列首行
{
f[t]=x;
if(t==m)
{
run();
return ;
}
dfs(t+1,0);
dfs(t+1,1);
}
int main()
{
while(cin>>n>>m)
{
ces=0;
mins=100000000;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
cin>>a[i][j];
}
dfs(0,0);
if(ces)
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
cout<<mtag[i][j]<<' ';
cout<<endl;
}
else
cout<<"IMPOSSIBLE"<<endl;
}
}