计蒜客习题:迷阵突围


问题描述

蒜头君陷入了坐标系上的一个迷阵,迷阵上有 n 个点,编号从 1 到 n。蒜头君在编号为 1 的位置,他想到编号为 n 的位置上。蒜头君当然想尽快到达目的地,但是他觉得最短的路径可能有风险,所以他会选择第二短的路径。现在蒜头君知道了 n 个点的坐标,以及哪些点之间是相连的,他想知道第二短的路径长度是多少。
注意,每条路径上不能重复经过同一个点。
输入格式
第一行输入两个整数 n (1≤n≤200) 和 m,表示一共有 n 个点和 m 条边。
接下来输入 n 行,每行输入两个整数xi,yi(−500≤xi,yi≤500),代表第 ii 个点的坐标。
接下来输入 mm 行,每行输入两个整数 pj,qj(1≤pj,qj≤n),表示点pj和点 qj之间相连。
输出格式
输出一行,输出包含一个数,表示第二短的路径长度(小数点后面保留两位),如果第一短路径有多条,则答案就是第一最短路径的长度;如果第二最短路径不存在,则输出 −1。
样例输入
3 3
1 1
2 2
3 2
1 2
2 3
1 3
样例输出
2.41


AC代码

原文传送门

#include<iostream>  
#include<cstdio>  
#include<set>  
#include<vector>  
#include<cmath>  
#include<cstring>  
using namespace std;  
const int maxn=207;  
const int maxm=50007;   //207*207  可能有重边 所以再开大一些 不给数据范围是真的睿智  
int n,m;  
int x[maxn],y[maxn],fa[maxn],q[maxn];  
double W(int u,int v);  
struct edge{  
    int v,nxt;  
    double w;  
}e[maxm<<1];        //睿智了 打成了maxn  
int p[maxn],eid;  
double dist[maxn];  
double ans=99999999;  
bool vst[maxn];  
//int id[maxn][maxn];  
typedef pair<double,int> pdi;  
set<pdi,less<pdi> >min_heap;  

void dijkstra(int a,int b);  
void ins(int u,int v);  
void ins2(int u,int v);  
void init();  
int main(){  
    cin>>n>>m;  
    init();  
    for(int i=1;i<=n;i++)  
        cin>>x[i]>>y[i];  
    for(int i=1;i<=m;i++)  
    {  
        int a,b;  
        cin>>a>>b;  
        ins2(a,b);  
    }  
    int now=n;  
    dijkstra(-1,-1);  
    while(fa[now]){  
        dijkstra(fa[now],now);  
        ans=min(dist[n],ans);  
        now=fa[now];  
    }  
   if(ans==99999999) printf("-1");  
    else printf("%.2lf",ans);  
    return 0;  
}  
double W(int u,int v){return sqrt((x[u]-x[v])*(x[u]-x[v])+(y[u]-y[v])*(y[u]-y[v]));}  
void dijkstra(int a,int b){  
    memset(vst,false,sizeof(vst));  
   for(int i=1;i<=n;i++) dist[i]=99999999;  
    dist[1]=0;  
    min_heap.insert(make_pair(0,1));  
    for(int i=1;i<=n;i++){  
        if(min_heap.size()==0) break;  
        auto iter=min_heap.begin();  
        int v=iter->second;  
        min_heap.erase(*iter);  
        if(vst[v]) continue;  
        vst[v]=true;  
        for(int j=p[v];j;j=e[j].nxt){   //枚举相邻边  
           if(v==a&&e[j].v==b||v==b&&e[j].v==a) continue;    //跳过最短路上的某条边  
            int x=e[j].v;  
            if(dist[x]>dist[v]+e[j].w){  
                min_heap.erase(make_pair(dist[x],x));  
                dist[x]=dist[v]+e[j].w;  
                min_heap.insert(make_pair(dist[x],x));  
                if(a==-1&&b==-1) fa[x]=v;  
            }  
        }  
    }  

}  
void ins(int u,int v){  
    e[++eid].v=v;  
    e[eid].nxt=p[u];  
    e[eid].w=W(u,v);  
    p[u]=eid;   //eid要从1开始  0开始会炸  
}  
void ins2(int u,int v){ins(u,v);ins(v,u);}  //无向边插两次  
void init(){  
    eid=0;  
    memset(p,0,sizeof(p));  
}  
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值