標籤:
http://acm.hdu.edu.cn/showproblem.php?pid=3567
相比Eight,似乎只是把目標狀態由確定的改成不確定的,但是康托展開+曼哈頓為h值的A*和IDA*都不過,而且也不好控制字典序
換個角度想,雖然起始狀態有很多,但是到底哪一位是1,哪一位是2不是最重要的,最重要的是和目標狀態對應,所以可以把起始狀態重新編碼為"12345678"這種形式(先不考慮X),然後目標狀態也對應過去,當考慮X的時候,我們可以認為起始狀態只有9種,分別是‘X‘在各個位置的情況
利用bfs可以得到每種起始狀態到對應的能夠達到的所有目標狀態的最短最小字典序轉移方法,如果對每個狀態都記錄一個字串肯定會MLE,所以只需要記錄上一個狀態到這一個狀態轉移所需要的操作即可
最後,對於每個目標狀態,逆向推出狀態轉移序列,然後逆著輸出即可
ps:一次處理3.6e6個狀態,如果不用queue或者迴圈數組會MLE,如果不用g++提交而用c++提交會TLE
#include <cstdio>#include <cstring>#include <algorithm>#include <queue>#include <cctype>using namespace std;const int base[9]={1,1,2,6,24,120,720,5040,40320};const int dx[4]={1,0,0,-1};/轉移方向,遵從字典序最小const int dy[4]={0,-1,1,0};const int maxh=362880;//每種開始狀態所對應狀態數int des;//用於儲存目標狀態hash值,判斷是否不需要轉移struct pnt{ int maz[3][3]; int ind;//標記起始狀態的編號 int x,y; int hashCode; pnt(){ x=y=hashCode=-1; } pnt(char * str){ for(int i=0;i<9;i++){ if(str[i]==‘X‘){ maz[i/3][i%3]=9; x=i/3; y=i%3; ind=i; } else maz[i/3][i%3]=str[i]-‘0‘; } } int gethashcode(){ int ans=0; for(int i=0;i<9;i++){ int cnt=0; for(int j=0;j<i;j++){ if(maz[j/3][j%3]>maz[i/3][i%3]){ cnt++; } } ans+=base[i]*cnt; } return hashCode=ind*maxh+ans; }};bool in(int tx,int ty){ return tx>=0&&tx<3&&ty>=0&&ty<3;}int vis[maxh*10+1];//用於儲存某狀態是否已被遍曆,並儲存由何種操作得到該狀態int pre[maxh*10+1];//用於儲存某狀態由何種狀態轉移得到queue<pnt> que;char ans[maxh*10+1];//儲存答案的逆序char buff[300];//儲存輸入pnt s,e;int ind[10],indlen;//用於重新編碼bool input(){ if(scanf("%s",buff)!=1)return false; indlen=0;//重新編碼 for(int i=0;i<9;i++){ if(buff[i]>=‘0‘&&buff[i]<=‘9‘){ s.maz[i/3][i%3]=++indlen; ind[buff[i]-‘0‘]=indlen; } else{ s.maz[i/3][i%3]=9; s.x=i/3; s.y=i%3; s.ind=i; } } if(scanf("%s",buff)!=1)return false; for(int i=0;i<9;i++){ if(buff[i]>=‘0‘&&buff[i]<=‘9‘){ e.maz[i/3][i%3]=ind[buff[i]-‘0‘]; } else{ e.maz[i/3][i%3]=9; e.x=i/3; e.y=i%3; } } e.ind=s.ind;//起始狀態相同 des=e.gethashcode(); return true;}void bfs(){ while(!que.empty()){ pnt tp=que.front();que.pop(); int x=tp.x,y=tp.y; for(int i=0;i<4;i++){ int tx=x+dx[i],ty=y+dy[i]; if(in(tx,ty)){ pnt nw=tp; swap(nw.maz[tx][ty],nw.maz[x][y]); nw.x=tx,nw.y=ty; if(vis[nw.gethashcode()]==-1){ vis[nw.hashCode]=i; pre[nw.hashCode]=tp.gethashcode(); que.push(nw); } } } }}void init(){ memset(vis,-1,sizeof(vis)); memset(pre,-1,sizeof(pre)); pnt s1=pnt("12345678X");//9種起始狀態加入序列 vis[s1.gethashcode()]=-2; que.push(s1); pnt s2=pnt("1234567X8"); vis[s2.gethashcode()]=-2; que.push(s2); pnt s3=pnt("123456X78"); vis[s3.gethashcode()]=-2; que.push(s3); pnt s4=pnt("12345X678"); vis[s4.gethashcode()]=-2; que.push(s4); pnt s5=pnt("1234X5678"); vis[s5.gethashcode()]=-2; que.push(s5); pnt s6=pnt("123X45678"); vis[s6.gethashcode()]=-2; que.push(s6); pnt s7=pnt("12X345678"); vis[s7.gethashcode()]=-2; que.push(s7); pnt s8=pnt("1X2345678"); vis[s8.gethashcode()]=-2; que.push(s8); pnt s9=pnt("X12345678"); vis[s9.gethashcode()]=-2; que.push(s9); bfs();}int heap[maxh],sz;int getans(pnt e){//逆序遍曆 sz=0; int last=e.gethashcode(); while(pre[last]>=0){ heap[sz++]=vis[last]; last=pre[last]; } return sz;}void print(){ for(int i=sz-1;i>=0;i--){ switch(heap[i]){ case 0: putchar(‘d‘);break; case 1: putchar(‘l‘);break; case 2: putchar(‘r‘);break; case 3: putchar(‘u‘);break; } } putchar(‘\n‘);}int main(){ int T; scanf("%d",&T); init(); for(int ti=1;ti<=T;ti++){ input(); s.hashCode=s.gethashcode(); if(s.hashCode==des){ printf("Case %d: 0\n",ti); puts(""); continue; } vis[s.hashCode]=-2; int step=getans(e); printf("Case %d: %d\n",ti,step); print(); } return 0;}
HDU 3567 Eight II 打表,康托展開,bfs,g++提交可過c++不可過 難度:3