UVA - 1601 The Morning after Halloween (BFS/雙向BFS/A*)
阿新 • • 發佈:2019-02-11
get 明顯 src 轉化 names space 需要 printf after
題目鏈接
挺有意思但是代碼巨惡心的一道最短路搜索題。
因為圖中的結點太多,應當首先考慮把隱式圖轉化成顯式圖,即對地圖中可以相互連通的點之間連邊,建立一個新圖(由於每步不需要每個鬼都移動,所以每個點需要向自己也連一條邊)。設d[i][j][k]為走到“A在結點i,B在結點j,C在結點k”的狀態需要多少步,直接bfs即可。
註意由於鬼的個數不確定,為了減少特判,需要留出三個虛節點,把多出來的鬼的起點和終點都設到同一個虛節點上。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef longbfslong ll; 4 const int N=16+2; 5 struct D {int a[3];}; 6 struct E {int v,nxt;} e[1000000]; 7 int rt[N][N],d[200][200][200],n,m,k,tot,ne,hd[200],bg[3],ed[3]; 8 char s[N][N]; 9 void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;} 10 bool ok(int* u,int* v) { 11 if(v[0]==v[1]||v[1]==v[2]||v[2]==v[0])return0; 12 if(u[0]==v[1]&&u[1]==v[0])return 0; 13 if(u[1]==v[2]&&u[2]==v[1])return 0; 14 if(u[2]==v[0]&&u[0]==v[2])return 0; 15 return 1; 16 } 17 18 int bfs() { 19 int u[3],v[3]; 20 queue<D> q; 21 q.push({bg[0],bg[1],bg[2]}),d[bg[0]][bg[1]][bg[2]]=0; 22 while(!q.empty()) { 23 memcpy(u,q.front().a,sizeof u),q.pop(); 24 if(u[0]==ed[0]&&u[1]==ed[1]&&u[2]==ed[2])return d[u[0]][u[1]][u[2]]; 25 for(int i=hd[u[0]]; ~i; i=e[i].nxt) 26 for(int j=hd[u[1]]; ~j; j=e[j].nxt) 27 for(int k=hd[u[2]]; ~k; k=e[k].nxt) { 28 v[0]=e[i].v,v[1]=e[j].v,v[2]=e[k].v; 29 if(ok(u,v)&&!~d[v[0]][v[1]][v[2]]) { 30 d[v[0]][v[1]][v[2]]=d[u[0]][u[1]][u[2]]+1; 31 q.push({v[0],v[1],v[2]}); 32 } 33 } 34 } 35 return -1; 36 } 37 38 int main() { 39 while(scanf("%d%d%d",&m,&n,&k)&&n) { 40 scanf(" "); 41 memset(hd,-1,sizeof hd),ne=0,tot=3; 42 for(int i=0; i<3; ++i)bg[i]=ed[i]=i; 43 addedge(0,0),addedge(1,1),addedge(2,2); 44 for(int i=0; i<n; ++i)gets(s[i]); 45 for(int i=1; i<n-1; ++i)for(int j=1; j<m-1; ++j)if(s[i][j]!=‘#‘) { 46 rt[i][j]=tot++; 47 addedge(rt[i][j],rt[i][j]); 48 if(s[i-1][j]!=‘#‘) { 49 addedge(rt[i][j],rt[i-1][j]); 50 addedge(rt[i-1][j],rt[i][j]); 51 } 52 if(s[i][j-1]!=‘#‘) { 53 addedge(rt[i][j],rt[i][j-1]); 54 addedge(rt[i][j-1],rt[i][j]); 55 } 56 if(isupper(s[i][j]))bg[s[i][j]-‘A‘]=rt[i][j]; 57 else if(islower(s[i][j]))ed[s[i][j]-‘a‘]=rt[i][j]; 58 } 59 for(int i=0; i<tot; ++i)for(int j=0; j<tot; ++j)for(int k=0; k<tot; ++k)d[i][j][k]=-1; 60 printf("%d\n",bfs()); 61 } 62 return 0; 63 }
這個代碼跑了1000+ms,我們可以繼續優化。
優化一:由於把一步移動撤回的規則和正向移動的規則是一樣的,因此可以把bfs改成雙向的,即6個鬼同時從起點和終點出發直到相遇,這樣可以降低bfs樹的深度,少擴展一些結點。方法是將d數組多開一維,代表每個狀態是正向轉移來的還是反向轉移來的。如果一個狀態的反狀態(對應鬼的位置均相等,bfs方向相反),則兩狀態的d值相加即為最短距離。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=16+2; 5 struct D {int f,a[3];}; 6 struct E {int v,nxt;} e[1000000]; 7 int rt[N][N],d[2][200][200][200],n,m,k,tot,ne,hd[200],bg[3],ed[3]; 8 char s[N][N]; 9 void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;} 10 bool ok(int* u,int* v) { 11 if(v[0]==v[1]||v[1]==v[2]||v[2]==v[0])return 0; 12 if(u[0]==v[1]&&u[1]==v[0])return 0; 13 if(u[1]==v[2]&&u[2]==v[1])return 0; 14 if(u[2]==v[0]&&u[0]==v[2])return 0; 15 return 1; 16 } 17 18 int bfs() { 19 int u[3],v[3],f; 20 queue<D> q; 21 q.push({0,bg[0],bg[1],bg[2]}),d[0][bg[0]][bg[1]][bg[2]]=0; 22 q.push({1,ed[0],ed[1],ed[2]}),d[1][ed[0]][ed[1]][ed[2]]=0; 23 while(!q.empty()) { 24 memcpy(u,q.front().a,sizeof u),f=q.front().f,q.pop(); 25 if(~d[f^1][u[0]][u[1]][u[2]])return d[f][u[0]][u[1]][u[2]]+d[f^1][u[0]][u[1]][u[2]]; 26 for(int i=hd[u[0]]; ~i; i=e[i].nxt) 27 for(int j=hd[u[1]]; ~j; j=e[j].nxt) 28 for(int k=hd[u[2]]; ~k; k=e[k].nxt) { 29 v[0]=e[i].v,v[1]=e[j].v,v[2]=e[k].v; 30 if(ok(u,v)&&!~d[f][v[0]][v[1]][v[2]]) { 31 d[f][v[0]][v[1]][v[2]]=d[f][u[0]][u[1]][u[2]]+1; 32 q.push({f,v[0],v[1],v[2]}); 33 } 34 } 35 } 36 return -1; 37 } 38 39 int main() { 40 while(scanf("%d%d%d",&m,&n,&k)&&n) { 41 scanf(" "); 42 memset(hd,-1,sizeof hd),ne=0,tot=3; 43 for(int i=0; i<3; ++i)bg[i]=ed[i]=i; 44 addedge(0,0),addedge(1,1),addedge(2,2); 45 for(int i=0; i<n; ++i)gets(s[i]); 46 for(int i=1; i<n-1; ++i)for(int j=1; j<m-1; ++j)if(s[i][j]!=‘#‘) { 47 rt[i][j]=tot++; 48 addedge(rt[i][j],rt[i][j]); 49 if(s[i-1][j]!=‘#‘) { 50 addedge(rt[i][j],rt[i-1][j]); 51 addedge(rt[i-1][j],rt[i][j]); 52 } 53 if(s[i][j-1]!=‘#‘) { 54 addedge(rt[i][j],rt[i][j-1]); 55 addedge(rt[i][j-1],rt[i][j]); 56 } 57 if(isupper(s[i][j]))bg[s[i][j]-‘A‘]=rt[i][j]; 58 else if(islower(s[i][j]))ed[s[i][j]-‘a‘]=rt[i][j]; 59 } 60 for(int i=0; i<tot; ++i)for(int j=0; j<tot; ++j)for(int k=0; k<tot; ++k)d[0][i][j][k]=d[1][i][j][k]=-1; 61 printf("%d\n",bfs()); 62 } 63 return 0; 64 }bfs(雙向)
跑了600+ms,感覺也沒快多少~~
優化二:可以考慮用A*算法,新開一個h數組記錄每個節點分別到a,b,c結點的最短距離(可用bfs預處理),則當前狀態(i,j,k)到(a,b,c)的最短距離不超過f[i][j][k]=d[i][j][k]+max(h[a][i],h[b][j],h[c][k]),選擇把原來的隊列換成優先隊列,每次取出f值最小的結點進行擴展即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=16+2; 5 struct E {int v,nxt;} e[1000000]; 6 int rt[N][N],d[200][200][200],h[3][200],n,m,k,tot,ne,hd[200],bg[3],ed[3]; 7 char s[N][N]; 8 struct D { 9 int a[3]; 10 bool operator<(const D& b)const { 11 return d[a[0]][a[1]][a[2]]+max(h[0][a[0]],max(h[1][a[1]],h[2][a[2]])) 12 >d[b.a[0]][b.a[1]][b.a[2]]+max(h[0][b.a[0]],max(h[1][b.a[1]],h[2][b.a[2]])); 13 } 14 }; 15 void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;} 16 bool ok(int* u,int* v) { 17 if(v[0]==v[1]||v[1]==v[2]||v[2]==v[0])return 0; 18 if(u[0]==v[1]&&u[1]==v[0])return 0; 19 if(u[1]==v[2]&&u[2]==v[1])return 0; 20 if(u[2]==v[0]&&u[0]==v[2])return 0; 21 return 1; 22 } 23 24 void bfs(int S,int* d) { 25 int u,v; 26 queue<int> q; 27 for(int i=0; i<tot; ++i)d[i]=-1; 28 q.push(S),d[S]=0; 29 while(!q.empty()) { 30 u=q.front(),q.pop(); 31 for(int i=hd[u]; ~i; i=e[i].nxt) { 32 v=e[i].v; 33 if(!~d[v])d[v]=d[u]+1,q.push(v); 34 } 35 } 36 } 37 38 int Astar() { 39 int u[3],v[3]; 40 priority_queue<D> q; 41 d[bg[0]][bg[1]][bg[2]]=0,q.push({bg[0],bg[1],bg[2]}); 42 while(!q.empty()) { 43 memcpy(u,q.top().a,sizeof u),q.pop(); 44 if(u[0]==ed[0]&&u[1]==ed[1]&&u[2]==ed[2])return d[u[0]][u[1]][u[2]]; 45 for(int i=hd[u[0]]; ~i; i=e[i].nxt) 46 for(int j=hd[u[1]]; ~j; j=e[j].nxt) 47 for(int k=hd[u[2]]; ~k; k=e[k].nxt) { 48 v[0]=e[i].v,v[1]=e[j].v,v[2]=e[k].v; 49 if(ok(u,v)&&!~d[v[0]][v[1]][v[2]]) { 50 d[v[0]][v[1]][v[2]]=d[u[0]][u[1]][u[2]]+1; 51 q.push({v[0],v[1],v[2]}); 52 } 53 } 54 } 55 return -1; 56 } 57 58 int main() { 59 while(scanf("%d%d%d",&m,&n,&k)&&n) { 60 scanf(" "); 61 memset(hd,-1,sizeof hd),ne=0,tot=3; 62 for(int i=0; i<3; ++i)bg[i]=ed[i]=i; 63 addedge(0,0),addedge(1,1),addedge(2,2); 64 for(int i=0; i<n; ++i)gets(s[i]); 65 for(int i=1; i<n-1; ++i)for(int j=1; j<m-1; ++j)if(s[i][j]!=‘#‘) { 66 rt[i][j]=tot++; 67 addedge(rt[i][j],rt[i][j]); 68 if(s[i-1][j]!=‘#‘) { 69 addedge(rt[i][j],rt[i-1][j]); 70 addedge(rt[i-1][j],rt[i][j]); 71 } 72 if(s[i][j-1]!=‘#‘) { 73 addedge(rt[i][j],rt[i][j-1]); 74 addedge(rt[i][j-1],rt[i][j]); 75 } 76 if(isupper(s[i][j]))bg[s[i][j]-‘A‘]=rt[i][j]; 77 else if(islower(s[i][j]))ed[s[i][j]-‘a‘]=rt[i][j]; 78 } 79 for(int i=0; i<tot; ++i)for(int j=0; j<tot; ++j)for(int k=0; k<tot; ++k)d[i][j][k]=-1; 80 for(int i=0; i<3; ++i)bfs(ed[i],h[i]); 81 printf("%d\n",Astar()); 82 } 83 return 0; 84 }A*
我用A*算法跑樣例的速度明顯快了好幾個檔次,但提交上去卻依舊跑了400+ms,看來A*終究逃不過被卡的命運~~
UVA - 1601 The Morning after Halloween (BFS/雙向BFS/A*)