Description
500年前,Jesse是我国最卓越的剑客。他英俊潇洒,而且机智过人^_^。
突然有一天,Jesse心爱的公主被魔王困在了一个巨大的迷宫中。Jesse听说这个消息已经是两天以后了,他急忙赶到迷宫,开始到处寻找公主的下落。令人头痛的是,Jesse是个没什么方向感的人,因此,他在行走过程中,不能转太多弯了,否则他会晕倒的。 我们假定Jesse和公主所在的位置都是空地,初始时,Jesse所面向的方向未定,他可以选择4个方向的任何一个出发,而不算成一次转弯。希望你帮他判断一下他是否有机会找到心爱的公主。
Input
题目包括多组测试数据.
第1行为一个整数T(1 ≤ T≤ 100),表示测试数据的个数,接下来为T组测试数据.
每组测试数据以两个整数N,M,K(1<=N, M≤100, 0<K<=10)开头,分别代表迷宫的高,长和Jesse最多能转的弯数,(紧接着有N行,M列字符,由".","*","P","S"组成。其中
"." 代表能够行走的空地。
"*" 代表墙壁,Jesse不能从此通过。
"P" 是公主所在的位置。
"S" 是Jesse的起始位置。
每个时间段里Jesse只能选择“上、下、左、右”任意一方向走一步。
Output
如果Jesse能在晕之前找到公主,输出“YES”,否则输出“NO”。
Sample Input
2
5 5 1
P..**
*.**.
S....
.....
*....
5 5 2
P..**
*.**.
S....
.....
*....
Sample Output
NO
YES
http://acm.tzc.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=3305
很久之前就过了这题了,当时是用dfs过的,我也觉得会超时的了,但是提交上去10ms过了,看来还是数据太弱了。必须会超时的,网上其他代码的dfs都会超时,我试过了
40 40 10
P.*.....................................
**......................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
.......................................S
这样他们的代码就超时了,关于dfs怎么做,应该很容易想的,用dfs(x,y,face,turn)表示在[x,y]那里,面向face那里,转弯次数是turn次的情况,直接dfs即可,唯一的剪枝也就是if (turn>k),就是已经晕了就不行了吧?对于我上面那组数据都超时了,就不用想100*100的矩阵了。
dfs AC代码(不是正解)
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define inf (1<<28) typedef long long int LL; #include <iostream> #include <sstream> #include <vector> #include <set> #include <map> #include <queue> #include <string> int flag; int n,m,k; const int maxn=1e2+20; char str[maxn][maxn]; int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; bool book[maxn][maxn]; void dfs (int x,int y,int face,int turn) { if (turn>k) return ;//已经晕了就不行了 if (str[x][y]=='P') { flag=1;//找到公主了 return ; } for (int i=0;i<4;i++) { int tx=x+next[i][0]; int ty=y+next[i][1]; if (!flag&&tx>=1&&tx<=n&&ty>=1&&ty<=m&&str[tx][ty]!='*'&&book[tx][ty]==0) { book[tx][ty]=1; if (face!=i) { dfs(tx,ty,i,turn+1); } else dfs(tx,ty,i,turn); book[tx][ty]=0; } } return ; } void work () { scanf ("%d%d%d",&n,&m,&k); for (int i=1;i<=n;i++) { scanf ("%s",str[i]+1); } int bx,by; for (int i=1;i<=n;i++) { for (int j=1;j<=m;j++) { if (str[i][j]=='S') { bx=i; by=j; } } } flag=0; for (int i=0;i<4;i++) { int tx=bx+next[i][0]; int ty=by+next[i][1]; if (tx>=1&&tx<=n&&ty>=1&&ty<=m&&str[tx][ty]!='*') { memset (book,0,sizeof(book)); if (str[tx][ty]=='P') { printf ("YES\n"); return ; } dfs(tx,ty,i,0); if (flag) { printf ("YES\n"); return ; } } } printf ("NO\n"); return ; } int main () { #ifdef local freopen("data.txt","r",stdin); #endif int t; scanf ("%d",&t); while (t--) { work (); } return 0; }
这题的正解应该是bfs,我开始的时候,写朴素的bfs无限WA,开始的时候想不出数据,就上网搜,用dfs过了。直到现在,才发现是要用优先队列优化一个bfs,每次pop出当前转弯次数最小的点来转移枚举,这样才能确保答案的最优性。关于怎么证明,我用一组样例来说明,是和本题无关的题目。
题意:大概是这样的,人物P要去救人物S,途中有警察X,没经过一个警察,就要杀死他,耗时+1,其他的,直接一步到达。现在问P-->S的最短时间。
直接用朴素的bfs是会wa的了。这个也应该用优先队列优化。因为bfs是有次序之分的。
.....
p....
.x...
.x...
.S...
明显这组样例输出的是4,但是bfs输出的是6,为什么呢?
首先得看你bfs的反向是怎样的,我的是右--下--左--上。
那么,第一个入队的点,是p右边那个,然后每次扩展的时候,第一个扩展的就是它了,走向了x(3,2),这是没问题的,因为现在走向x的点,步数都是最小的。但是问题来了。走向x(4,2)这个点的会是谁呢?答案是x(3,2),为什么呢?因为它先入队,所以先搜它。照这样下去,走向S的点,就会是x(4,2)了。所以最优性错误。
下面的bfs正解代码
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define inf (1<<28) typedef long long int LL; #include <iostream> #include <sstream> #include <vector> #include <set> #include <map> #include <queue> #include <string> int n,m,k; const int maxn=1e2+20; char str[maxn][maxn]; int next[4][2]= {{0,1},{1,0},{0,-1},{-1,0}}; struct coor { int x,y,count; int face; friend bool operator < (const coor a,const coor b) { return a.count>b.count; } }; int lbx,lby; int bfs (int bx,int by,int face) { bool book[maxn][maxn]= {0}; book[lbx][lby]=1; book[bx][by]=1; priority_queue<struct coor>que; struct coor t; struct coor b; t.x=bx; t.y=by; t.count=0; t.face=face; que.push(t); while (!que.empty()) { t=que.top(); que.pop(); if (t.count>k) continue; for (int i=0; i<4; i++) { b.x=t.x+next[i][0]; b.y=t.y+next[i][1]; if (b.x>=1&&b.x<=n&&b.y>=1&&b.y<=m&&str[b.x][b.y]!='*'&&book[b.x][b.y]==0) { book[b.x][b.y]=1; if (t.face!=i) { b.count=t.count+1; } else b.count=t.count; b.face=i; if (str[b.x][b.y]=='P') { return b.count; } que.push(b); } } } return inf; } void work () { scanf ("%d%d%d",&n,&m,&k); for (int i=1; i<=n; i++) { scanf ("%s",str[i]+1); } for (int i=1; i<=n; i++) { for (int j=1; j<=m; j++) { if (str[i][j]=='S') { lbx=i; lby=j; } } } for (int i=0; i<4; i++) { int tx=lbx+next[i][0]; int ty=lby+next[i][1]; if (tx>=1&&tx<=n&&ty>=1&&ty<=m&&str[tx][ty]!='*') { if (str[tx][ty]=='P') { printf ("YES\n"); return ; } int ans=bfs(tx,ty,i); if (ans<=k) { //printf ("%d\n",i); printf ("YES\n"); return ; } //printf ("%d %d\n",ans,i); } } printf ("NO\n"); return ; } int main () { #ifdef local freopen("data.txt","r",stdin); #endif int t; scanf ("%d",&t); while (t--) { work (); } return 0; }
还有我看到有人说这题只能用dfs不能用bfs,那是错误的。希望其他人别用dfs了。用bfs吧。这才是正解。
本人初出茅庐,如果有哪里错误的地方,还请读者多多指出,本人感激不尽!
来源:https://www.cnblogs.com/liuweimingcprogram/p/5497969.html