hdu 1242 Rescue

  题目链接:hdu 1242

  这题也是迷宫类搜索,题意说的是 'a' 表示被拯救的人,'r' 表示搜救者(注意可能有多个),'.' 表示道路(耗费一单位时间通过),'#' 表示墙壁,'x' 代表警卫(耗费两个单位时间通过),然后求出 'r' 能找到 'a' 的最短时间,找不到输出 "…………"(竟然在这里也 wa 了一发 -.-||)。很明显是广搜了,因为 'r' 可能有多个,所以我们反过来从 'a' 开始搜,每次搜到 'r' 都更新最小时间值(很重要的一个转换!)。可是这题因为通过 'x' 需要 2 单位时间导致结点间的变化不一致,所以不能用简单的队列(等下会否定这个说法),需要用优先队列,再加上 bfs 就行。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<queue>
 5 #include<algorithm>
 6 using namespace std;
 7 #define For(i,s,t)    for(int i=s; i<t; ++i)
 8 const int inf= 0x2fffffff;
 9 
10 int n,m;
11 char s[206][206];
12 int dir[6][2]= {{-1,0},{0,-1},{0,1},{1,0}};
13 
14 struct node{
15     int x,y,t;
16     node(int x=0, int y=0, int t= inf): x(x),y(y),t(t) {}
17     bool operator < (const node &n2) const {
18         return t > n2.t;
19     }
20 };
21 
22 int main(){
23     int si,sj;
24     while(~scanf("%d%d",&n,&m)){
25         getchar();
26         For(i,0,n+2)    For(j,0,m+2)    s[i][j]= '#';
27         for(int i=1; i<=n; ++i,getchar())
28             For(j,1,m+1)    if((s[i][j]= getchar())=='a')    si=i,    sj= j;
29         
30         int ans= 500;
31         priority_queue<node> q;
32         q.push(node(si,sj,0));
33         s[si][sj]= '#';
34         while(q.size()){
35             node p= q.top();    q.pop();
36             For(k,0,4){
37                 int dx= p.x+dir[k][0], dy= p.y+dir[k][1];
38                 if(s[dx][dy]!='#'){
39                     q.push(node(dx,dy,(s[dx][dy]=='x'? p.t+2: p.t+1)));
40                     if(s[dx][dy]=='r')    ans= min(ans,p.t+1);
41                     s[dx][dy]= '#';
42                 }
43             }
44         }
45         if(ans==500)    puts("Poor ANGEL has to stay in the prison all his life.");
46         else    printf("%d\n",ans);
47     }
48     return 0;
49 }

  这一句 " if(s[dx][dy]=='r') ans= min(ans,p.t+1); " 对 'r' 的访问和更新需要很注意位置,一定要放在 " s[dx][dy]= '#'; " 即对已访问结点做标记之前,否则 ans 的值不会变的。

  上面是优先队列+bfs,然后这题实际上还可以用加了特技的普通队列来实现 bfs。怎么做呢,仔细观察这题每两个结点间的(边)权值实际上只有两种:1和2。2对应的就是 'x' 这个结点,只要做特殊处理就行:把它一分为二,分成两个结点 'y' 和 '.' 。大体步骤是:对每个出队的结点访问它的后继结点,当为 'x' 时,把它标记为 'y',和其它结点一样边权值+1入队,如果为 'y',那么就直接跳过,因为 'y' 本来就是从 'x' 转化过来的,并非从它旁边的结点扩展过来;这样的话,每次从队首出对的结点就只有两种可能:'y' 和 '#'(对于 '.' 和 'r' 入队后做的标记),如果是 'y' 的话,就把结点权值+1 再次进队(因为 'x' 本来就耗时 2),然后该结点才真正标记为 '#' 表示确实地访问完,这样的话就能用普通的队列来实现 bfs了,主要就是把那些权值不一致的结点通过拆分成多个等价的结点以转化为权值一致的图。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<queue>
 5 #include<algorithm>
 6 using namespace std;
 7 #define For(i,s,t)    for(int i=s; i<t; ++i)
 8 const int inf= 0x2fffffff;
 9 
10 int n,m;
11 char s[206][206];
12 int dir[6][2]= {{-1,0},{0,-1},{0,1},{1,0}};
13 
14 struct node{
15     int x,y,t;
16     node(int x=0, int y=0, int t= inf): x(x),y(y),t(t) {}
17 };
18 
19 int main(){
20     int si,sj;
21     while(~scanf("%d%d",&n,&m)){
22         getchar();
23         For(i,0,n+2)    For(j,0,m+2)    s[i][j]= '#';
24         for(int i=1; i<=n; ++i,getchar())
25             For(j,1,m+1)    if((s[i][j]= getchar())=='a')    si=i,    sj= j;
26         
27         int ans= 500;
28         queue<node> q;
29         q.push(node(si,sj,0));
30         s[si][sj]= '#';
31         while(q.size()){
32             node p= q.front();    q.pop();
33             if(s[p.x][p.y]=='y'){
34                 q.push(node(p.x,p.y,p.t+1));
35                 s[p.x][p.y]= '#';
36                 continue;
37             }
38             For(k,0,4){
39                 int dx= p.x+dir[k][0], dy= p.y+dir[k][1];
40                 if(s[dx][dy]=='y')    continue;
41                 if(s[dx][dy]!='#'){
42                     q.push(node(dx,dy,p.t+1));
43                     if(s[dx][dy]=='x') {
44                         s[dx][dy]= 'y';
45                         continue;
46                     }
47                     if(s[dx][dy]=='r')    ans= min(ans,p.t+1);
48                     s[dx][dy]= '#';
49                 }
50             }
51         }
52         if(ans==500)    puts("Poor ANGEL has to stay in the prison all his life.");
53         else    printf("%d\n",ans);
54     }
55     return 0;
56 }

  当然,这个如此巧妙的方法是从叉姐那儿听说过来的,我不过尝试着把它实现了而已,只能膜拜那些 OI 大神了 Orz ~~

  另外,很奇怪的是,这题居然也能用 dfs 来做:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<algorithm>
 5 using namespace std;
 6 #define For(i,s,t)  for(int i=s; i<t; ++i)
 7 const int maxn= 200+3;
 8 
 9 char ch[maxn][maxn];
10 int dir[2][4]= {{-1,0,0,1},{0,-1,1,0}};
11 int Min, num;
12 
13 void dfs(int i, int j, int len)
14 {
15     ++num;
16     if(ch[i][j]=='#')    return ;
17     if(ch[i][j]=='x')    ++len;
18     if(len > Min)    return ;
19     if(ch[i][j]=='r'){
20         Min= min(Min,len);
21         return ;
22     }
23     char c= ch[i][j];
24     ch[i][j]= '#';
25     For(k,0,4) {
26         int dx= i+dir[0][k], dy= j+dir[1][k];
27         dfs(dx,dy,len+1);
28     }
29     ch[i][j]= c;
30 }
31 
32 int main()
33 {
34     int n,m,si,sj;
35     while(~scanf("%d%d",&n,&m))
36     {
37         getchar();
38         memset(ch,'#',sizeof(ch));
39         for(int i=1; i<=n; ++i,getchar())
40             For(j,1,m+1)    if((ch[i][j]= getchar())=='a')    si= i,    sj=j;
41         
42         Min= 500;
43         dfs(si,sj,0);
44         if(Min==500)    puts("Poor ANGEL has to stay in the prison all his life.");
45         else    printf("%d\n",Min);
46     }
47     return 0;
48 }
View Code

  对于这样的题来说 dfs 应该很容易超时才对,可是实际上和 bfs 相差无几,只能再一次吐槽下杭电数据的弱了,anyway,我还是会继续被 HDOJ 虐……

posted @ 2015-04-08 10:55  Newdawn_ALM  阅读(181)  评论(0编辑  收藏  举报