问题 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;
}
写的不好,请见谅,谢谢