题目描述
在七夕过后不久,Czy把hzwer的后宫们抢走不少……hzw怒了,要冲到czy的的后宫里找他算账。
但是czy的后宫是风水宝地,三面环山一面环水,按太极五行八卦二十八宿之势,隐隐有王八之气侧漏。依山傍水易守难攻,前有“良将劲弩守要害之处”,后有“信臣精卒陈利兵而谁何”。总之黄巨大只能从水路上去就对了。
那是一片n*m的水域……只要从(x1,y2)走到位置(x2,y2)的传送阵中就好啦……但是hzwer不(tai)够(shen)强(xu),没法在水上如履平地……幸好水上有若干个岩石是可以站在上面的,而且可以站无限多次。但是同样的有若干个点有漩涡、激流或者奇怪的生物(比如zzz)出没……总之这些点不能走。hzwer昨晚把妹过多,所以他摇摇晃晃的只能一步走长度为的路径,并且只能站在整点上。当然hzwer比我强多了,所以他还有一个特殊的技能——可以在水上跳,跳来跳去的在跳(太跳了),但是每次走到水上跳起来都会消耗1mp和若干rp,所以他要尽快的通过这块水域,以便留足mp和rp去痛扁czy……但是他有点虚,请你告诉他最少要消耗多少mp才能通过这块水域,还有消耗最少mp的情况下最少他要走几步,以及消耗最少mp且步数最少的情况下他有多少种方案。
输入描述
第一行两个数n,m
接下来n行,每行m个数,表示整个水域的地图
1:这是水
2:这是石头
3:危险!zzz出没!
4:黄巨大从这里出发
5:传送阵在这里
输出描述
一行或三行
如果无论如何hzwer都到不了传送阵,输出一行-1
否则输出三行:
第一行:最小mp值
第二行:最少走几步
第三行:在步数限制下,有多少种方案
样例输入1:
4 8
1 1 1 2 1 1 1 1
1 1 1 1 1 3 1 2
1 1 1 1 1 5 1 1
4 1 1 1 1 1 2 1
样例输出1:
2
6
2
样例输入2:
2 3
4 1 1
1 1 5
样例输出2:
0
1
1
样例输入3:
2 3
1 4 1
1 1 5
样例输出3:
-1
数据范围
对于10%数据,n,m<=10,不存在zzz
对于20%数据,n,m<=20,不存在zzz
对于40%数据,n,m<=20
对于60%数据,n,m<=30
对于80%数据,n,m<=100
对于98%数据,n,m<=200
对于100%数据,n,m<=300
疑惑:
这道题是搜索,我只得了50+,而我错在一个很奇葩的位置,但我不知道怎么证明,请各位神犇赐教。
问题:1、在做计算步数的bfs时,队列中待拓展的点的步数是否严格不降。
2、在这道题中,队列中待拓展的点的步数是否严格不降。
3、如果2的答案是yes,那么请问在这道题中,为什么需要在前后两次搜到同一个点时,对两次的步数进行分类讨论。
否则只有50+分。
如果2的答案是no,那么请问在“求让马跳到目的地的最小步数”一题中,为什么只要bfs的过程中终点第一次被搜
到就可当做答案,那道题和这道题有什么异同点。
如果神犇需要测试数据,可以在评论中叫我。
以下是我的提交程序:50+分。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<algorithm>
#define inf 1<<30
using namespace std;
int n,m,map[302][302],xs,ys,xe,ye;
int xx[8]={-2,-2,-1,-1,1,1,2,2},yy[8]={-1,1,-2,2,-2,2,-1,1};
int mp[302][302],//记录mp。
ct[302][302],//方案数。
f[302][302],//步数。
pd[302][302];//是否在队列中。
struct dui{int x,y;} q[90002];
void init()
{
scanf("%d%d",&n,&m);
int i,j,x;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{scanf("%d",&x);
if(x==1) map[i][j]=1;
else if(x==2) map[i][j]=0;
else if(x==3) map[i][j]=-1;
else if(x==4)
{map[i][j]=0; xs=i; ys=j;}
else
{map[i][j]=0; xe=i; ye=j;}
}
}
bool judge(int x,int y)
{
if(x<1||x>n||y<1||y>m||map[x][y]==-1) return false;
return true;
}
void bfs()
{
int t=0,w=1,i,j,xn,yn,xt,yt,step;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{mp[i][j]=inf; f[i][j]=inf;}
mp[xs][ys]=0; ct[xs][ys]=1; f[xs][ys]=0;
q[0].x=xs; q[0].y=ys; pd[xs][ys]=1;
while(t!=w)//用循环队列做bfs。
{xn=q[t].x; yn=q[t].y;
t=(t+1)%90002;
for(i=0;i<8;i++)
{xt=xn+xx[i]; yt=yn+yy[i];
if(xt<1||xt>n||yt<1||yt>m||map[xt][yt]==-1) continue;
step=map[xt][yt];
if(mp[xt][yt]>step+mp[xn][yn])
{mp[xt][yt]=step+mp[xn][yn];
ct[xt][yt]=ct[xn][yn];
f[xt][yt]=f[xn][yn]+1;
if(!pd[xt][yt])
{q[w].x=xt; q[w].y=yt; pd[xt][yt]=1;
w=(w+1)%90002;
}
}
else if(mp[xt][yt]==step+mp[xn][yn])
/*我对这一段的理解是: 因为为bfs,在bfs的过程中,步数一定不断+0或1,即f不降
对于同一个点,在bfs中第二次搜到,所用步数一定不比之前那次搜到小,所以这里
我没有分类讨论f。
*/
{ct[xt][yt]+=ct[xn][yn];
/*if(!pd[xt][yt])
{q[w].x=xt; q[w].y=yt; pd[xt][yt]=1;
w=(w+1)%90002;
}
这一段加上之后程序会死循环。 为什么?
*/
}
}
pd[xn][yn]=0;
}
if(ct[xe][ye]==0) {printf("-1\n"); return;}
printf("%d\n%d\n%d\n",mp[xe][ye],f[xe][ye],ct[xe][ye]);
}
int main()
{
//freopen("zouwei.in","r",stdin);
//freopen("zouwei.out","w",stdout);
init();
bfs();
return 0;
}
以下是正解。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<algorithm>
#define inf 1<<30
#define ll long long
using namespace std;
int n,m,map[302][302],xs,ys,xe,ye;
int xx[8]={-2,-2,-1,-1,1,1,2,2},yy[8]={-1,1,-2,2,-2,2,-1,1};
int mp[302][302],//记录mp。
f[302][302],//步数。
pd[302][302];//是否在队列中。
ll ct[302][302];//方案数。
struct dui{int x,y;} q[90002];
void init()
{
scanf("%d%d",&n,&m);
int i,j,x;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{scanf("%d",&x);
if(x==1) map[i][j]=1;
else if(x==2) map[i][j]=0;
else if(x==3) map[i][j]=-1;
else if(x==4)
{map[i][j]=0; xs=i; ys=j;}
else
{map[i][j]=0; xe=i; ye=j;}
}
}
bool judge(int x,int y)
{
if(x<1||x>n||y<1||y>m||map[x][y]==-1) return false;
return true;
}
void bfs()
{
int t=0,w=1,i,j,xn,yn,xt,yt,step;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{mp[i][j]=inf; f[i][j]=inf;}
mp[xs][ys]=0; ct[xs][ys]=1; f[xs][ys]=0;
q[0].x=xs; q[0].y=ys; pd[xs][ys]=1;
while(t!=w)
{xn=q[t].x; yn=q[t].y;
t=(t+1)%90002;
for(i=0;i<8;i++)
{xt=xn+xx[i]; yt=yn+yy[i];
if(xt<1||xt>n||yt<1||yt>m||map[xt][yt]==-1) continue;
//printf("%d %d\n",xt,yt);
step=map[xt][yt];
//printf("%d %d %d\n",step,mp[xn][yn],mp[xt][yt]);
if(mp[xt][yt]>step+mp[xn][yn])
{mp[xt][yt]=step+mp[xn][yn];
ct[xt][yt]=ct[xn][yn];
f[xt][yt]=f[xn][yn]+1;
if(!pd[xt][yt])
{q[w].x=xt; q[w].y=yt; pd[xt][yt]=1;
w=(w+1)%90002;
}
}
else if(mp[xt][yt]==step+mp[xn][yn])
//加了对步数f的特判就a了。 而此时再判断是否入队列就没有问题了。
{if(f[xn][yn]+1<f[xt][yt])
{f[xt][yt]=f[xn][yn]+1;
ct[xt][yt]=ct[xn][yn];
if(!pd[xt][yt])
{q[w].x=xt; q[w].y=yt; pd[xt][yt]=1;
w=(w+1)%90002;
}
}
else if(f[xn][yn]+1==f[xt][yt])
{ct[xt][yt]+=ct[xn][yn];
if(!pd[xt][yt])
{q[w].x=xt; q[w].y=yt; pd[xt][yt]=1;
w=(w+1)%90002;
}
}
}
}
pd[xn][yn]=0;
}
if(ct[xe][ye]==0) {printf("-1\n"); return;}
printf("%d\n%d\n%lld\n",mp[xe][ye],f[xe][ye],ct[xe][ye]);
}
int main()
{
init();
bfs();
return 0;
}