poj3279 Fliptile (dfs搜索)

题意:一个地图,每个格子不是黑色(1)就是白色(0),通过翻转使所有格子都变成白色,翻转时上下左右也会被同时翻转,求最小翻转方法,多个最优方法时,输出字典序最小的。

思路:想明白了其实很简单。枚举第一行所有翻转情况,注意字典序,然后根据前一行的结果判断下一行的某一个是否需要翻转。比如第一行翻转完后,第一个是1,那么接下来能改变它的只有第二行第一个,所以它必须翻转。最后判断最后一行是否符合要求,如果符合,记录下最短的。

还有一个问题,刚开始我是用bool记录颜色的,研究了好久怎么操作,输入可以用int,但是会比较麻烦(也可以先判断,再赋值),不如直接用int保存。char我也试过,就是空格输入是个问题。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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;
}

碰到这种题目,还是要心细,不急。

你可能感兴趣的:(搜索,入门训练,acm,c++,搜索,poj,dfs)