luogu P5304 [GXOI/GZOI2019]旅行者

本文深入探讨了SMG暴力算法的实现细节,通过枚举关键点进行最短路径计算,解决特定路径查找问题。文章详细介绍了如何处理起点与终点相同的特殊情况,通过记录最短路径及其起点,以及次短路径来优化算法,确保正确性和效率。
摘要由CSDN通过智能技术生成

传送门

所以这个\(5s\)是SMG

暴力是枚举每一个点跑最短路,然后有一个很拿衣服幼稚的想法,就是把所有给出的关键点当出发点,都丢到队列里,求最短路的时候如果当前点\(x\)某个相邻的点\(y\)是关键点,就用\(dis_x+\)边权\(w_i\)更新答案.感觉这个复杂度是正确的,然后跑一下样例也对

交上去就可以获得70'的好成绩

这个方法会有一种特殊情况无法处理,就是这条路径的起点和终点都是同一点,因为图中可能有环.那么我们更新答案就不能用起点是\(y\)的路径更新答案,于是考虑同时记录从某个出发点到一个点的最短路以及是从哪个出发点转移过来的,同时求次短路,要求最短路的起点和次短路起点不一样,那么在更新答案时,如果最短路起点不等于终点就用最短路更新,否则用次短路

// luogu-judger-enable-o2
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<cmath>
#include<ctime>
#include<queue>
#include<map>
#include<set>
#define LL long long
#define db double

using namespace std;
const int N=100000+10,M=500000+10;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int to[M],nt[M],hd[N],tot;
LL w[M];
void add(int x,int y,int z){++tot,to[tot]=y,nt[tot]=hd[x],w[tot]=z,hd[x]=tot;}
bool v[N];
struct nn
{
    LL d;
    int y;
    void clr(){d=1ll<<50,y=0;}
    bool operator < (const nn &bb) const {return d<bb.d;}
}d1[N],d2[N];
struct node
{
    int x;
    nn d1,d2;
    bool operator < (const node &bb) const {return bb.d1.d!=d1.d?bb.d1<d1:bb.d2<d2;}
};
priority_queue<node> q;
int n,m,kk;

int main()
{
    int T=rd();
    while(T--)
    {
        memset(hd,0,sizeof(hd)),tot=0;
        n=rd(),m=rd(),kk=rd();
        for(int i=1;i<=m;++i)
        {
            int x=rd(),y=rd(),z=rd();
            if(x==y) continue;
            add(x,y,z);
        }
        memset(v,0,sizeof(v));
        for(int i=1;i<=n;++i) d1[i].clr(),d2[i].clr();
        LL ans=1ll<<50;
        while(kk--)
        {
            int x=rd();
            d1[x]=(nn){0,x},v[x]=1,q.push((node){x,d1[x],d2[x]});
        }
        while(!q.empty())
        {
            int x=q.top().x;
            nn dd1=q.top().d1,dd2=q.top().d2;
            q.pop();
            if(d1[x]<dd1||d2[x]<dd2) continue;
            for(int i=hd[x];i;i=nt[i])
            {
                int y=to[i];
                if(v[y])
                {
                    if(dd1.y!=y) ans=min(ans,dd1.d+w[i]);
                    else ans=min(ans,dd2.d+w[i]);
                }
                else
                {
                    nn n1=d1[y],n2=d2[y];
                    if(n1.d>dd2.d+w[i])
                    {
                        if(n1.y!=n2.y) n2=n1;
                        n1=(nn){dd2.d+w[i],dd2.y};
                    }
                    else if(n2.d>dd2.d+w[i]&&n1.y!=dd2.y) n2=(nn){dd2.d+w[i],dd2.y};
                    if(n1.d>dd1.d+w[i])
                    {
                        if(n1.y!=n2.y) n2=n1;
                        n1=(nn){dd1.d+w[i],dd1.y};
                    }
                    else if(n2.d>dd1.d+w[i]&&n1.y!=dd1.y) n2=(nn){dd1.d+w[i],dd1.y};
                    if(n1<d1[y]||n2<d2[y]) q.push((node){y,d1[y]=n1,d2[y]=n2});
                }
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/smyjr/p/10763331.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值