SWUST power oj 2862(最短路问题,优先队列优化dij)

SWUST power oj 2862

\bullet Description

小Y在星际旅行的过程中,意外的发现了一个神奇的圆形空间,当他的飞船进入这个空间之后,他可以在这个圆形的区域内任意飞行而不需要消耗能量。而当他的飞船不在这个圆形区域内时,消耗的能量与行驶的距离在数值上相等。在小Y的旅行途中,一共有 n个这样神奇的空间。小Y想知道,他从第s个圆上某个点出发,到达第t个圆上某个点需要的最小能量消耗。每个神奇的空间都会被表示成一个二维平面上的圆。

\bullet Input
第一行一个整数 T ( T 100 ) T(T≤100) ,表示测试组数
对于每组测试样例:
第一行一个整数 n ( 1 n 50 ) n (1≤n≤50) ,表示神奇空间的个数
第二行四个整数 s x , s y , t x , t y ( 104 s x , s y , t x , t y 104 ) sx,sy,tx,ty (−104≤sx,sy,tx,ty≤104) ( s x , s y ) (sx,sy) 表示起点坐标, ( t x , t y ) (tx,ty) 表示终点坐标。数据保证起点和终点均是给出的 n n 个圆上的某一个点
接下来 n n 行,每行三个整数 x , y , r x,y,r ,数据保证给定的 n 个圆两两之间一定相离。
\bullet Output

对于每组样例,输出从起点到终点的最小能量消耗。
如果你的答案是 a,正确答案是 b ,只有当 a b m a x ( 1 , b ) 1 0 6 \frac{|a−b|}{max(1,|b|)}≤10^{−6} 时才会被认为是正确答案。

\bullet Sample Input

1
3
1 0 10 0
1 1 1
10 1 1
5 5 3

\bullet Sample Output

4.059978

思路:按输入顺序把每个圆编号为1~N,然后在输入圆的位置坐标和半径后就判断起点是哪个点,终点是哪个点,然后用链式向量星建双向有权图,然后再跑迪杰斯特拉或者spfa,floyd也可以但是我不想写了,优先队列优化迪杰斯特拉在后面。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<iostream>
#include<queue>
#include<string>
#include<math.h>
typedef long long LL;
using namespace std;
const int maxn=2e4;
const int inf=999999999;
const int mo=1000000007;
double sx,sy,tx,ty;
int cnt=0,n;
int head[maxn],vis[maxn];
double d[maxn];
struct node///链式向前星
{
    int to;
    double val;
    int next;
}e[maxn];
struct info///存储初始点
{
    double x;
    double y;
    double r;
}p[maxn];

void add(int start,int End,double val)///链式向前星
{
    e[++cnt]={End,val,head[start]};
    head[start]=cnt;
}

void init()///初始化
{
    cnt=0;
    memset(p,0,sizeof(p));
    memset(e,0,sizeof(e));
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++)
        d[i]=99999999999999.9;
}

double getval(double x1,double y1,double x2,double y2,double r1,double r2)///计算两个圆之间的有效距离
{
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))-(r1+r2);
}

bool dis(double x1,double y1,double x2,double y2,double r)///判断起点和终点
{
    double ans=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    if(ans<=r)return true;
    else return false;
}

void dij(int start)///迪杰斯特拉算法
{
    d[start]=0;
    for(int i=1;i<=n;i++){
        int minpos=-1;
        for(int j=1;j<=n;j++){
            if((vis[j]==0)&&(minpos<0||d[j]<d[minpos]))
                minpos=j;
        }
        vis[minpos]=1;
        //if(minpos==-1)break;
        for(int i=head[minpos];i!=-1;i=e[i].next){
            int to=e[i].to;
            double val=e[i].val;
            if(d[minpos]+val<d[to])
                d[to]=d[minpos]+val;
        }
    }
}

void spfa(int start)///spfa算法
{
    queue<int>q;
    q.push(start);
    vis[start]=1;
    d[start]=0;
    while(!q.empty()){
        int now=q.front();q.pop();
        vis[now]=0;
        for(int i=head[now];i!=-1;i=e[i].next){
            int to=e[i].to;
            double val=e[i].val;
            if(d[now]+val<d[to]){
                d[to]=d[now]+val;
                if(vis[to]==0){
                    vis[to]=1;
                    q.push(to);
                }
            }
        }
    }
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        scanf("%lf%lf%lf%lf",&sx,&sy,&tx,&ty);
        init();
        double x,y,r;
        int start,End;///记录起点和终点
        for(int i=1;i<=n;i++){///把圆域抽象成1~n的点
            scanf("%lf%lf%lf",&x,&y,&r);
            p[i]=info{x,y,r};
            if(dis(sx,sy,x,y,r))///判断起点
                start=i;
            if(dis(tx,ty,x,y,r))///判断终点
                End=i;
        }
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                double val=getval(p[i].x,p[i].y,p[j].x,p[j].y,p[i].r,p[j].r);///计算两个圆之间的有效距离
                add(i,j,val);///加双向边
                add(j,i,val);
            }
        }
        dij(start);
        printf("%.9f\n",d[End]);
    }
    return 0;
}

\bullet 再加一个优先队列优化迪杰斯特拉的代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<iostream>
#include<queue>
#include<string>
#include<math.h>
typedef long long LL;
using namespace std;
const int maxn=2e4;
const int inf=999999999;
const int mo=1000000007;
double sx,sy,tx,ty;
int cnt=0,n;
int head[maxn],vis[maxn];
double d[maxn];

struct dis
{
    int id;
    double d;
    bool operator < (const dis &a)const//重载小于号
    {
        return d>a.d;
    }
}a,b;priority_queue<dis>q;

struct node///链式向前星
{
    int to;
    double val;
    int next;
}e[maxn];
struct info///存储初始点
{
    double x;
    double y;
    double r;
}p[maxn];

void add(int start,int End,double val)///链式向前星
{
    e[++cnt]={End,val,head[start]};
    head[start]=cnt;
}

void init()///初始化
{
    cnt=0;
    memset(p,0,sizeof(p));
    memset(e,0,sizeof(e));
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++)
        d[i]=99999999999999.9;
    while(!q.empty())q.pop();
}

double getval(double x1,double y1,double x2,double y2,double r1,double r2)///计算两个圆之间的有效距离
{
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))-(r1+r2);
}

bool dis(double x1,double y1,double x2,double y2,double r)///判断起点和终点
{
    double ans=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    if(ans<=r)return true;
    else return false;
}


void SDIJ(int start)///优先队列优化
{
    a={start,0.0};
    q.push(a);
    d[start]=0;
    while(!q.empty()){
        a=q.top();q.pop();
        int now=a.id;
        double dnow=a.d;
        vis[now]=1;
        for(int i=head[now];i!=-1;i=e[i].next){
            int to=e[i].to;
            double val=e[i].val;
            if(dnow+val<d[to]){
                d[to]=dnow+val;
                b={to,d[to]};
                q.push(b);
            }
        }
    }
}


int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        scanf("%lf%lf%lf%lf",&sx,&sy,&tx,&ty);
        init();
        double x,y,r;
        int start,End;///记录起点和终点
        for(int i=1;i<=n;i++){///把圆域抽象成1~n的点
            scanf("%lf%lf%lf",&x,&y,&r);
            p[i]=info{x,y,r};
            if(dis(sx,sy,x,y,r))///判断起点
                start=i;
            if(dis(tx,ty,x,y,r))///判断终点
                End=i;
        }
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                double val=getval(p[i].x,p[i].y,p[j].x,p[j].y,p[i].r,p[j].r);///计算两个圆之间的有效距离
                add(i,j,val);///加双向边
                add(j,i,val);
            }
        }
        SDIJ(start);
        printf("%.9f\n",d[End]);
    }
    return 0;
}


发布了34 篇原创文章 · 获赞 7 · 访问量 1889

猜你喜欢

转载自blog.csdn.net/qq_43628761/article/details/96007699