2018年北京信息科技大学第十届程序设计竞赛暨ACM选拔赛:A-PUBG

链接: https://www.nowcoder.com/acm/contest/118/A
来源:牛客网

最近,喜爱ACM的PBY同学沉迷吃鸡,无法自拔,于是又来到了熟悉的ERANGEL。经过一番搜寻,PBY同学准备动身前往安全区,但是,地图中埋伏了许多LYB,PBY的枪法很差,希望你能够帮他找到一条路线,每次只能向上、下、左、右移动,尽可能遇到较少的敌人。

输入描述:

题目包含多组测试,请处理到文件结束;
第一行是一个整数n,代表地图的大小;
接下来的n行中,每行包含n个整数a,每个数字a代表当前位置敌人的数量;
1 < n <= 100,1 <= a <= 100,-1代表当前位置,-2代表安全区。

输出描述:

对于每组测试数据,请输出从当前位置到安全区所遇到最少的敌人数量,每个输出占一行。
示例1

输入

5
6 6 0 -2 3
4 2 1 2 1
2 2 8 9 7
8 1 2 1 -1
9 7 2 1 2

输出

9
示例2

输入

5
62 33 18 -2 85
85 73 69 59 83
44 38 84 96 55
-1 11 90 34 50
19 73 45 53 95

输出

173

这题有两种写法 1.SPFA(有负权路径 不能用dijkstra) 2.DFS暴力搜索(会超时)

了解spfa的同学可以跳过这段 不了解的同学 可以看一下:

转自https://blog.csdn.net/xunalove/article/details/70045815

   求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm。 
    SPFA算法是西南交通大学段凡丁于1994年发表的。
    从名字我们就可以看出,这种算法在效率上一定有过人之处。 
    很多时候,给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。有人称spfa算法是最短路的万能算法。

    简洁起见,我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路。
    我们用数组dis记录每个结点的最短路径估计值,可以用邻接矩阵或邻接表来存储图G,推荐使用邻接表。

spfa的算法思想(动态逼近法):
    设立一个先进先出的队列q用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。 
    松弛操作的原理是著名的定理:“三角形两边之和大于第三边”,在信息学中我们叫它三角不等式。所谓对结点i,j进行松弛,就是判定是否dis[j]>dis[i]+w[i,j],如果该式成立则将dis[j]减小到dis[i]+w[i,j],否则不动。 
    下面举一个实例来说明SFFA算法是怎样进行的:




和广搜bfs的区别:
    SPFA 在形式上和广度(宽度)优先搜索非常类似,不同的是bfs中一个点出了队列就不可能重新进入队列,但是SPFA中一个点可能在出队列之后再次被放入队列,也就是一个点改进过其它的点之后,过了一段时间可能本身被改进(重新入队),于是再次用来改进其它的点,这样反复迭代下去。

算法的描述:

void  spfa(s);  //求单源点s到其它各顶点的最短距离
    for i=1 to n do { dis[i]=∞; vis[i]=false; }   //初始化每点到s的距离,不在队列
    dis[s]=0;  //将dis[源点]设为0
    vis[s]=true; //源点s入队列
    head=0; tail=1; q[tail]=s; //源点s入队, 头尾指针赋初值
    while head<tail do {
       head+1;  //队首出队
       v=q[head];  //队首结点v
       vis[v]=false;  //释放对v的标记,可以重新入队
       for 每条边(v,i)  //对于与队首v相连的每一条边
  	  if (dis[i]>dis[v]+a[v][i])  //如果不满足三角形性质
	 	dis[i] = dis[v] + a[v][i]   //松弛dis[i]
		if (vis[i]=false) {tail+1; q[tail]=i; vis[i]=true;} //不在队列,则加入队列
    } 
 

本题代码如下:

#include <iostream>
#include <string.h>
#include <queue>
 
using namespace std;
 
#define INF 0x3f3f3f3f
 
int n,sx,sy,ex,ey;
const int maxn=105;
int dis[maxn][maxn];
int vis[maxn][maxn];
int dt[maxn][maxn];
const int di_x[4]={1,-1,0,0};
const int di_y[4]={0,0,-1,1};
void init(){
	memset(dis,1,sizeof(dis));//memset作用与一个字节之上
	//变为00000001 00000001 00000001 00000001 
	memset(vis,0,sizeof(vis));
}
void spfa(int x,int y){
	queue<int> vx,vy;
	vx.push(x);
	vy.push(y);
	dis[x][y]=0;
	vis[x][y]=1;
	while(!vx.empty()){
		int dx,dy;
		dx=vx.front();
		dy=vy.front();
		vis[dx][dy]=0;
		for(int i=0;i<4;i++){
			int newx,newy;
			newx=dx+di_x[i];
			newy=dy+di_y[i];
			if(newx<1||newx>n||newy<1||newy>n) continue;
			if(dis[newx][newy]>dt[newx][newy]+dis[dx][dy]){
				dis[newx][newy]=dt[newx][newy]+dis[dx][dy];
				if(vis[newx][newy]==0){
					vis[newx][newy]=1;
					vx.push(newx);
					vy.push(newy);
				}
        	}
		}
		vx.pop();
		vy.pop();
}
}
int main()
{
    while(cin >> n)
    {
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=n;++j)
            {
                cin >> dt[i][j];
                if(dt[i][j]==-1)
                    sx=i,sy=j,dt[i][j]=0;
                else if(dt[i][j]==-2)
                    ex=i,ey=j,dt[i][j]=0;
            }
        }
        init();
        //dfs(sx,sy,0);
        spfa(sx,sy);
        cout << dis[ex][ey] << endl;
    }
    return 0;
}

用DFS的写法如下:

void dfs(int x,int y,int step)
{
    if(dt[x][y]==-2)
    {
        if(ans>step)
            ans=step;
        return;
    }
    vis[x][y]=1;
    for(int d=0;d<4;++d)
    {
        int nx=x+mx[d];
        int ny=y+my[d];
        if(nx>=1 && nx<=n && ny>=1 && ny<=n)
        {
            if(!vis[nx][ny] && step+dt[x][y]<ans)
                dfs(nx,ny,step+dt[x][y]);
        }
    }
    vis[x][y]=0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值