poj 1753 Flip Game & poj2965

用位操作+BFS+枚举解决.基本思想如下:
给棋盘每一个状态赋予一个状态id,id计算方法是将棋盘与数的二进制表示联系起来,如题所给的数据:
bwwb
bbwb
bwwb
bwww
状态id为6585,计算方法为1*2^0+0*2^1+0*2^2..1*2^12+0*2^13..=6585(其中b代表1,w代表0)
在此基础上进行BFS搜索,
1)总的状态数是一定的,共0~~65535=65536种状态;
2)首先理解一点,先点(0,0)再点(0,1)与先点(0,1)再点(0,0)对结果不造成任何影响.因此遍历棋盘的16个位置,将每次点击后的状态id利用树状结构保存.如:
                                 6585
                               /   |   \  ...
                           (0,0) (0,1)  (0,2)
                            /      |      \  ...
                         6568     6553     6646
                      ...............................
对此树进行BFS搜索,将id为0(全白)或65535(全黑)的时候则搜索成功,输出树的高度当队列为空,仍当未搜索成功时,则输出"Impossible".

#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <string.h>
#include <iostream>
#include <map>
#include <vector>
#include <queue>
#include <set>
#include <string>
#include <math.h>
#define N 1000000
using namespace std;
//BFS+枚举有限的状态数:2^16=65536种
int que[N];
int flag[65536];
int step[65536];
int front=0,rear=0;
//翻转某位,只需将该位与1抑或
int flip(int state,int pos)  //翻转pos及其周围位置
{
   state^=(1<<pos);
   if((pos-4)>=0)   state^=(1<<(pos-4));   //翻转上方
   if((pos+4)<=15)  state^=(1<<(pos+4));   //翻转下方
   if(pos%4!=0)     state^=(1<<(pos-1));   //翻转左方
   if(pos%4!=3)     state^=(1<<(pos+1));   //翻转右方
   return state;
}
//广搜
int bfs()
{
   int tag=0,state,temp;
   while(front<rear){                                     //当队列不为空
     state=que[front++];
     for(int i=0;i<16;i++){                         //枚举当前状态下,翻转16个棋子后的状态           
        temp=flip(state,i);
        if(temp==0||temp==65535){  //全0或全1          
           tag=1;
           flag[temp]=1;
           step[temp]=step[state]+1;
           break;
        }
        else if(!flag[temp]){
           que[rear++]=temp;
           flag[temp]=1;
           step[temp]=step[state]+1;  
        }
     }
     if(tag)  break;               
   }
   return tag;
}
 
int main()
{
  int i,j;
  int state=0;
  char s[5];
  for(i=0;i<4;i++){
    scanf("%s",s);
    for(j=0;j<4;j++){
      if(s[j]=='b')                          //令'b'=1,'w'=0,当该位为0时,不需加
          state|=(1<<(i*4+j));  //该位为1时,state+=1^位置              
    }
  }
  if(state==0||state==65535)  printf("0\n");
  else{
    que[rear++]=state;
    flag[state]=1;
    memset(step,0,sizeof(step));
    if(bfs())     printf("%d\n",flag[0]==1?step[0]:step[65535]);       
    else            printf("Impossible\n");
  }
  return 0;
}
这道题是搜了题解之后才写出来的,刚开始总是不知道该如何枚举,忽略了状态数是有限的这一条件。
做完这道,再去做poj2965 http://poj.org/problem?id=2965 ,就简单多了。唯一不同的就是多了保存搜索路径;我能想到的就是保存当前状态的前一个状态,以及前一个状态到达当前状态的路径值;总体感觉空间效率有点儿低。。。附上代码:
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <string.h>
#include <iostream>
#include <map>
#include <vector>
#include <queue>
#include <set>
#include <string>
#include <math.h>
#define N 1000000
using namespace std;
char s[5];
int que[N];
int flag[65536];
int step[65536];
struct stpath{
  int prest;
  int ps;       
}pp[65536];   //保存路径
struct path{
  int x;
  int y;       
}r[65536];
int initial()    //初始化 
{
  int state=0;
  for(int i=0;i<4;i++){
    scanf("%s",s);
    for(int j=0;j<4;j++){
      if(s[j]=='+')
        state|=1<<(i*4+j);     //求初始状态                        
    }        
  } 
  return state;      
}
int switching(int temp,int pos)   //状态翻转 
{
     temp^=(1<<pos);
     int x=pos/4;
     int y=pos%4;
     for(int j=0;j<4;j++){  //翻转row x 
        int pr=x*4+j;
        if(pr!=pos)  temp^=(1<<pr);     
     } 
     for(int i=0;i<4;i++){  //翻转col Y 
        int pc=i*4+y;
        if(pc!=pos)  temp^=(1<<pc);     
     }
     return temp;
} 
int bfs(int state){     //广度优先搜索 
  int front=0,rear=0;
  que[rear++]=state;
  flag[state]=1;
  while(front<rear){
    int pstate=que[front++];
    for(int i=0;i<16;i++){
      int temp=switching(pstate,i);
      if(temp==0){
        pp[temp].prest=pstate;   //保存前一个状态 
        pp[temp].ps=i;           //保存状态翻转路径 
        step[temp]=step[pstate]+1;
        flag[temp]=1;
        return step[0];            
      }  
      else if(!flag[temp]){
        pp[temp].prest=pstate;   //保存前一个状态 
        pp[temp].ps=i;           //保存状态翻转路径 
        que[rear++]=temp;
        step[temp]=step[pstate]+1;
        flag[temp]=1;
      }                 
    }                                     
  }   
}
void savePath(){    //保存路径 
  int k=0; 
  int temp=0;
  while(pp[temp].prest!=-1){
    r[k].x=pp[temp].ps/4+1;
    r[k++].y=pp[temp].ps%4+1;
    temp=pp[temp].prest;
  }
  return;    
}
int main()
{
  int k,state,temp;
  state=initial();
  memset(step,0,sizeof(step));
  pp[state].prest=-1;
  k=bfs(state);
  savePath();
  printf("%d\n",k);
  for(int i=k-1;i>=0;i--){
    printf("%d %d\n",r[i].x,r[i].y);       
  }
  //system("pause");
  return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章