题意:一个地图,每个格子不是黑色(1)就是白色(0),通过翻转使所有格子都变成白色,翻转时上下左右也会被同时翻转,求最小翻转方法,多个最优方法时,输出字典序最小的。
思路:想明白了其实很简单。枚举第一行所有翻转情况,注意字典序,然后根据前一行的结果判断下一行的某一个是否需要翻转。比如第一行翻转完后,第一个是1,那么接下来能改变它的只有第二行第一个,所以它必须翻转。最后判断最后一行是否符合要求,如果符合,记录下最短的。
还有一个问题,刚开始我是用bool记录颜色的,研究了好久怎么操作,输入可以用int,但是会比较麻烦(也可以先判断,再赋值),不如直接用int保存。char我也试过,就是空格输入是个问题。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <algorithm>
#include <cstring>
#include <utility>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
int m,n,minc;
struct node
{
int col; //颜色
int op; //操作
}mp[20][20],minway[20][20]; //一个是目前的情况,一个保存结果
void flip(int x,int y)
{
mp[x][y].col^=1;
mp[x-1][y].col^=1;
mp[x+1][y].col^=1;
mp[x][y-1].col^=1;
mp[x][y+1].col^=1;
mp[x][y].op^=1; //!!刚开始写的=1.没考虑到还原的情况
}
void cal(int x,int c) //x表示行数,c表示翻转次数
{
if(c>=minc) return; //剪枝
if(x>m)
{
for(int i=1;i<=n;i++)
if(mp[m][i].col) //IMPOSSIBLE
return;
minc=c;
memcpy(minway,mp,sizeof(mp));
return;
}
for(int i=1;i<=n;i++)
{
if(mp[x-1][i].col)
{
flip(x,i); //都根据上一行判断
c++;
}
}
cal(x+1,c);
for(int i=1;i<=n;i++) //还原
if(mp[x+1][i].op)
flip(x+1,i);
}
void dfs(int y,int c) //y记录列数,c记录翻转次数
{
if(c>=minc) return; //剪枝。因为已经按字典序排了,所以排除=的情况
if(y>n) //所有情况列举完,进行后续计算
{
cal(2,c);
return;
}
else
{
dfs(y+1,c);
flip(1,y);
dfs(y+1,c+1);
flip(1,y); //还原
}
}
int main()
{
while(scanf("%d%d",&m,&n)!=EOF)
{
minc=INF;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
scanf("%d",&mp[i][j].col);
mp[i][j].op=0; //多文件输入要初始化
}
dfs(1,0); //搜索第一行翻转情况
if(minc==INF)
printf("IMPOSSIBLE\n");
else
{
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
printf("%d",minway[i][j].op); //输出也需要花点心思
if(j!=n)
printf(" ");
else printf("\n");
}
}
}
return 0;
}
碰到这种题目,还是要心细,不急。