firefox SPFA最短路

问题 A: Firefox

题目描述

 山火来了,Fox们遇到了前所未有的危机,他们需要马上撤离这个区域。但是居住在离山火区域较远的Fox仍然不知道危险降临。这个时候,前方的Fox要用叫声来提醒其他的Fox。Fox的视野为S米,叫声最远能传到D米之外。山上有p处着火的地点,当着火点在Fox的视野中时,Fox就会发出叫声(进入视野是指Fox与火点距离<=S)。因为山风,声音传播的速度为v米/秒。那么如果要使所有的Fox都得到危险信号,至少需要多长时间?

      假设着火点为一个整数坐标,着火点不会移动也不会扩大。Fox听到同伴叫声就会立刻把信号传递下去。

输入

 第1行两个整数n,p,表示Fox数量和着火点数量

      第2行三个整数 S,D,v

      第3~p+2行,每行两整数x,y表示着火点坐标

      接下来n行每行两个整数x,y表示Fox坐标

输出

使所有Fox都得到信号所需的最短时间,答案为四舍五入之后的整数。题目保证所有Fox最终都会得到信号,不存在Fox哑嗓子或者被山火烧死的可能。

样例输入

2 12 2 10 00 20 4

样例输出

2

提示

对于100%的数据 n<=2000   p<=10   s,d,v<=1000

 

坐标绝对值小于等于10000

 

先抄个题目,虽然看起来很难,但其实就是简单的求单源最短路。

思路

 

我们先看数据范围,n在2000以内,所以可以使用邻接矩阵,时间复杂度O(n^2)也没有太大问题,那么我们开始考虑如何求解。

设节点为结构体,储存节点的x,y:qp[i][j]表示i到j的距离:ans[i]表示i收到信号时的最短时间:

开始时我们将所有的qp[i][j]和ans[i]赋成INF(因为距离不可能超过30000,于是我就赋成了30000)

首先我们开个虚拟节点(0),把FOX的节点号编为(1~n),把山火的节点号编为(n+1~n+p)。

然后读入山火位置,山火到虚拟节点的距离为0,直接连上。

接着在读入过程中开始判断,如果第i个FOX能够看到山火,那就让qp[i][山火编号]=0(因为是连接矩阵,所以记得双向赋边);

如果第i个FOX能够听到第j个FOX的信号,那么让qp[i][j]=两人间距离/速度;

最后以虚拟节点(0)为源点,开始跑最短路,最后把ans[1~n]中最大值即为答案。

(最短路可以用SPFA)

来份代码:

#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
struct poi{
	double x,y;
}z[2100];
double ans[2100];//每只FOX的最后最短收到信号的时间 
double qp[2100][2100];//i的信号传到j的时间 
bool pd[2100];//SPFA判断是否进入队列 
double jl(int a,int b){//求x到y的距离 
	return sqrt((z[a].x-z[b].x)*(z[a].x-z[b].x)+(z[a].y-z[b].y)*(z[a].y-z[b].y));
}
int dl[21000],l,r;//SPFA的队列,队头,队尾
int n,p;
void spfa(int x){
	r++;
	dl[r]=x;//将源点入队 
	int x1,y1;
	pd[x]=1;
	while(l<r){//开始循环,直到队空 
		x1=dl[++l];
		pd[x1]=0;//标记出队 
		for(int i=1;i<=n+p;i++){
			if(ans[i]>ans[x1]+qp[x1][i]){
				ans[i]=ans[x1]+qp[x1][i];//如果能更新答案,就更新 
				if(!pd[i]){//如果它没在队列中,则将它加入队列 
				dl[++r]=i;
				pd[i]=1;//标记入列 
				}
			}
		}
	}
}
int main(){
	scanf("%d%d",&n,&p);
	double s,d,v;//使用double,方便计算 
	scanf("%lf%lf%lf",&s,&d,&v);
	for(int i=0;i<=n+p;i++){//预处理,赋极大值 
		for(int j=0;j<=n+p;j++){
			qp[i][j]=1.00*30000;
		}
		qp[i][i]=0;//自己到自己距离为零 
		ans[i]=1.00*30000;
	}
	for(int i=1;i<=p;i++){
		scanf("%lf%lf",&z[n+i].x,&z[n+i].y);
		qp[0][n+i]=qp[n+i][0]=0;//连接火山点 
	}
	for(int i=1;i<=n;i++){
		scanf("%lf%lf",&z[i].x,&z[i].y);
		for(int j=1;j<=p;j++){
			if(jl(n+j,i)<=s){//判断是否与山火点相连 
				qp[n+j][i]=qp[i][n+j]=0;
			}
		}
		for(int j=1;j<=i;j++){
			if(jl(i,j)<=d){//是否能收到来自j的信号 
				qp[i][j]=qp[j][i]=min(qp[i][j],jl(i,j)/v);
			}
		}
	}
	ans[0]=0;//将源点答案赋为零 
	spfa(0);//调用SPFA 
	double ans1=0;
	for(int i=1;i<=n;i++){
		ans1=max(ans[i],ans1);//寻找最大值 
	}
	printf("%.0lf",ans1);//答案四舍五入 
	return 0;
}

写的不好,请见谅,谢谢

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值