1. 程式人生 > >UVA - 1601 The Morning after Halloween (BFS/雙向BFS/A*)

UVA - 1601 The Morning after Halloween (BFS/雙向BFS/A*)

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 long
long 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])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]; 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 }
bfs

這個代碼跑了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*)