洛谷 LUOGU P1629 邮递员送信 最短路问题

洛谷logo洛谷 LUOGU P1629 邮递员送信 题解


写在前面:

先给传送门:https://www.luogu.org/problem/show?pid=1629#sub
向洛谷众大佬势力低头,感谢洛谷ID: 点击试试手气 大佬的题解,让我收益颇丰。滑天下之大稽

题目:

题目描述:
有一个邮递员要送东西,邮局在节点1.他总共要送N-1样东西,其目的地分别是2~N。由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要一定的时间。这个邮递员每次只能带一样东西。求送完这N-1样东西并且最终回到邮局最少需要多少时间。

输入格式:
第一行包括两个整数N和M。
第2到第M+1行,每行三个数字U、V、W,表示从A到B有一条需要W时间的道路。 满足1<=U,V<=N,1<=W<=10000,输入保证任意两点都能互相到达。

输出格式:
输出仅一行,包含一个整数,为最少需要的时间。

数据规模:
对于30%的数据,有1≤N≤200;
对于100%的数据,有1≤N≤1000,1≤M≤100000。

思路 :

显然可得这是一道最短路问题,很普通也很特殊。普通在数据范围非常正常,甚至MAXN只有1000,比起好些模版题都不知道低到哪里去,我还跟他谈笑风生所以SPFA算法便可以很好的解决。但是题目特殊在最后需要的并不是单一两点间的最短路,而是1到2~N每个点来回最短路程的总和,所以需要以1点为起点做一次SPFA,得到1点到每个点的最短路。而后处理每个点到1之间的最短路。很惭愧,本蒟蒻在处理这个地方的时候还是犯了too young too simple sometimes N…的错误,选择了暴力求解的方式,暴力的做了N次SPFA……结果果然TLE了三组数据。所以,经过反复琢磨和参看大佬们的题解滑天下之大稽后做出了优化。将边反向,以求得N个点到1的最短距离,首先运用三个数组U,V,W记录输入的参数,在跑完1到每个点的最短路后,清空vis数组和存储路径信息的vector,初始化dist数组,(Tips:各位初次写没初始化的萌新,这里的初始化很重要!!),然后对U,V,W进行遍历,本来为U->V边权为W的路径在这里方向进行反向存储了!!现在要存储的应该是V->U边权为W的路径。这样再一次以1为起点进行一次SPFA,便可以的到每个点到1的最短路(Tips:这里相当于将所有路径反向,也就相当于把1作为终点去找每个点为起点时的返程的最短路)。废话不多说了,先上代码表尊重。滑天下之大稽

代码 :

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int MAXN=1010,MAXM=100010,s=1;
bool vis[MAXN];
vector <int> r[MAXN];
vector <int> w[MAXN];
vector <int> re_r[MAXN];
int dist[MAXN];
int m,n,U[MAXM],V[MAXM],W[MAXM],u;
void SPFA(){
    queue <int> que;
    dist[s]=0;
    vis[s]=true;
    que.push(s);
    while(!que.empty()){
        u=que.front();
        que.pop();
        vis[u]=false;
        for(int i=0;i<r[u].size();i++){
            if(dist[r[u][i]]>dist[u]+w[u][i]){
                dist[r[u][i]]=dist[u]+w[u][i];
                if(!vis[r[u][i]]){
                    que.push(r[u][i]);
                    vis[r[u][i]]=true;
                }
            }
        }
    }
}
int readint(){
    int k=0;
    char ch=getchar();
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9'){
        k=k*10+ch-'0';
        ch=getchar();
    }
    return k;
}
int main(){
    int cnt=0;
    memset(dist,126/3,sizeof dist);
    memset(vis,0,sizeof vis);
    n=readint(),m=readint();
    for(int i=1;i<=m;i++){
        U[i]=readint(),V[i]=readint(),W[i]=readint();
        r[U[i]].push_back(V[i]);
        w[U[i]].push_back(W[i]);
    }
    SPFA();
    for(int i=1;i<=n;i++){
        cnt+=dist[i];
    }
    //再次初始化
    memset(dist,126/3,sizeof dist);
    memset(vis,0,sizeof vis);
    for(int i=1;i<=n;i++){
        r[i].clear();
        w[i].clear();
    }
    //把每条边边反向记录
    for(int i=1;i<=m;i++){
        r[V[i]].push_back(U[i]);
        w[V[i]].push_back(W[i]);
    }
    SPFA();
    for(int i=1;i<=n;i++){
        cnt+=dist[i];
    }
    printf("%d",cnt);
}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值