瘋狂的馬兒 時間限制(普通/Java) : 1000 MS/ 3000 MS 運行記憶體限制 : 65536 KByte 總提交 : 19 測試通過 : 5 描述 在一個5*5規模的棋盤上,放著黑馬和白馬。每種馬都有12個,因此棋盤上恰好有一個空格。在任意時刻,馬兒只允許移動到空格上(移動規則如所示) 現在,給定棋盤的初始狀態,Snow_storm想知道最少移動幾次可以達到如所示的目標狀態: 輸入 輸入的第一行為正整數T(<=100),表示測試組數。 對於每組測試資料,共有5行,每行有5個字元,表示棋盤的初始狀態:其中’0’表示白馬,’1’表示黑馬,’ ’表示空格。 其中範例一對應的狀態如所示: 輸出 對於每組測試資料,若不能在16步之內(<16)達到目標狀態,則輸出Crazy horse,否則輸出最小步數。更多的細節請參看範例輸出。
範例輸入 2 01011 110 1 01110 01010 00100 10110 01 11 10111 01001 00000 範例輸出 Case 1: Crazy horse Case 2: 7 題目來源 YB |
題目地址:
http://218.194.91.48/acmhome/problemdetail.do?&method=showdetail&id=1425
原題:http://www.lightoj.com 題號1143 。。。 PS: 此題在輸出時對原題做了改動。
===================================================================================================
其實只要有想法,這題的代碼還是比較好實現的。
初步寫的時候,就是用B哥說的,KM匹配作為啟發學習法,寫IDA*。
KM匹配只看了一點。所以讓怪叔叔代敲(直接套B哥的模版)。。。
匹配的建圖自己建,一開始對於每次匹配都重新BFS一次建圖。。。
結果TLE在第一組測試資料。。。 於是預先處理所有位置到任意位置的步數,此時匹配的建圖時間就只有O(5*5*12)。。。
再次TLE在第二組測試資料。。。
一開始想了個剪枝: 不走回頭路。當你往前走時,記住此時走的方向,當你在下一個位置,判斷一下,不走反方向。。。
但是仍然TLE在第二組,貌似剪枝沒效果。。。
只能選項組,判斷重複狀態了。。。 不懂B哥選項組的方法(貌似與後向星有關?)
想了一下,突然想到用字典樹。。。囧。。。 時間夠了,範例卻過不去,13步能走的,走了15步。。。
然後想了一下,找到了一個BUG: DFS是深搜,也就是這個方向第5步產生的狀態,也許另一個方向只需要3步就能產生。。。
所以狀態判斷不能只判斷是否曾走過,還要判步數少的能再次走。。。
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;char p[6][6];// dfs時的當前地圖 // KM匹配 ===================================const int maxn=33;const int inf=0x3fffffff;int tol,n,m,mat[maxn],lx[maxn],ly[maxn],lack,map[maxn][maxn];bool visx[maxn],visy[maxn];bool Find(int x){ int i,v,y; visx[x]=true; for(i=1;i<=n;i++){ y=i; if(!visy[y]){ int t=lx[x]+ly[y]-map[x][y]; if(!t){ visy[y]=true; if(mat[y]==-1||Find(mat[y])){ mat[y]=x; return true; } } else if(lack>t) lack=t; } } return false;}int KM(){ int i,j,x,y; /*// 輸出當前地圖,與構圖結果 cout<<"p------"<<endl;for(i=0;i<5;i++){puts(p[i]);}cout<<"map------"<<endl;for(i=1;i<=n;i++){for(j=1;j<=m;j++)cout<<map[i][j]<<" ";cout<<endl;}*/ memset(mat,-1,sizeof(mat)); for(i=1;i<=n;i++){ lx[i]=-inf; ly[i]=0; for(j=1;j<=n;j++){ x=map[i][j]; if(x>lx[i]) lx[i]=x; } } for(i=1;i<=n;i++){ while(1){ memset(visx,false,sizeof(visx)); memset(visy,false,sizeof(visy)); lack=inf; if(Find(i)) break; for(j=1;j<=n;j++){ if(visx[j]) lx[j]-=lack; if(visy[j]) ly[j]+=lack; } } } int ret=0; for(i=1;i<=n;i++){ if(mat[i]!=-1) ret+=lx[mat[i]],ret+=ly[i]; } return ret;}// 構圖(用於KM匹配) ============ int fx[]={-2,2,-2,2,1,-1,1,-1};int fy[]={-1,1,1,-1,2,-2,-2,2};int d1[5][5]={{1,2,3,4,5},{0,6,7,8,9},{0,0,0,10,11},{0,0,0,0,12},{0,0,0,0,0}};int d2[5][5]={{0,0,0,0,0},{12,0,0,0,0},{10,11,0,0,0},{6,7,8,9,0},{1,2,3,4,5}};int rec[6][6][6][6];struct Node{int x,y;Node(){}Node(int a,int b){x=a,y=b;}}que[20000];void deal(){// 預先處理任意座標(x1,y1)到任意座標(x2,y2)之間的最短步數 int i,j,vis[6][6],ss,st,cnt=1,nx,ny,k,tx,ty;for(i=0;i<5;i++)for(j=0;j<5;j++){memset(vis,-1,sizeof(vis));que[ss=st=0]=Node(i,j);vis[i][j]=0;while(ss<=st){nx=que[ss].x;ny=que[ss].y;ss++;if(d1[nx][ny]!=0)rec[i][j][nx][ny]=-vis[nx][ny];if(d2[nx][ny]!=0)rec[i][j][nx][ny]=-vis[nx][ny];for(k=0;k<8;k++){tx=nx+fx[k];ty=ny+fy[k];if(tx<0 || ty<0 || tx>4 || ty>4) continue;if(vis[tx][ty]!=-1) continue;vis[tx][ty]=vis[nx][ny]+1;st++;que[st].x=tx;que[st].y=ty;}}cnt++;}}int BFS1(){// 匹配黑馬 int i,j,g,f,cnt=1;for(i=0;i<5;i++)for(j=0;j<5;j++)if(p[i][j]=='1'){for(g=0;g<5;g++)for(f=(g<2?g:g+1);f<5;f++)map[cnt][d1[g][f]]=rec[i][j][g][f];cnt++;}n=12,m=12;return KM();}int BFS2(){// 匹配白馬 int i,j,g,f,cnt=1;for(i=0;i<5;i++)for(j=0;j<5;j++)if(p[i][j]=='0'){for(g=0;g<5;g++)for(f=(g>2?g:g-1);f>=0;f--)map[cnt][d2[g][f]]=rec[i][j][g][f];cnt++;}n=12,m=12;return KM();}// 字典樹(用於儲存狀態) ================== struct Tree{int step;Tree *next[3];};Tree *root;void init_(Tree *x){// 初始化新節點 x->step=10000;x->next[0]=x->next[1]=x->next[2]=0;}int cover(int step){// 加入字典樹 Tree *new_,*now=root;int i,j,k,flag=0;for(i=0;i<5;i++)for(j=0;j<5;j++){if(p[i][j]=='0') k=0;else if(p[i][j]=='1') k=1;else k=2;if(now->next[k]==0){new_=new Tree;init_(new_);now->next[k]=new_;}now=now->next[k];}if(now->step>step){// 判斷可否再走 now->step=step;flag=1;}return flag;}void clr(Tree *x){// 刪除字典樹 (釋放記憶體) if(x->next[0]) clr(x->next[0]);if(x->next[1]) clr(x->next[1]);if(x->next[2]) clr(x->next[2]);delete x;}// 啟發學習法搜尋 ============= bool dfs(int x,int y,int step,int tol){int k,h,nx,ny;h=-(BFS1()+BFS2());// 啟發學習法 if(h==0) return true;if(h+step>tol) return false;for(k=0;k<8;k++){nx=x+fx[k];ny=y+fy[k];if(nx<0 || ny<0 || nx>4 || ny>4) continue;swap(p[nx][ny],p[x][y]);if(cover(step+1))// 查詢此狀態是否曾走過 if(dfs(nx,ny,step+1,tol)) return true;swap(p[nx][ny],p[x][y]);}return false;}// 主函數 =============== int main(){int tt,t,si,sj,i,j;bool res;char mp[6][6];deal();scanf("%d",&t);getchar();for(tt=1;tt<=t;tt++){for(i=0;i<5;i++){gets(mp[i]);memcpy(p[i],mp[i],5);for(j=0;j<5;j++)if(mp[i][j]==' ')si=i,sj=j;}i=BFS1()+BFS2();if(i<16)for(i=0;i<16;i++){// DIA*for(j=0;j<5;j++)memcpy(p[j],mp[j],5);root=new Tree;// 建立字典樹 init_(root);cover(0);res=dfs(si,sj,0,i);clr(root);if(res) break;}printf("Case %d: ",tt);if(i<16) printf("%d\n",i);else puts("Crazy horse");}return 0;}/*100111011111111100 0000000*/