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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章