求解!!! 模拟赛:你看他走位多好啊

题目描述

在七夕过后不久,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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值