路径和树

题目来源:  CodeForces
基准时间限制:1.5 秒 空间限制:131072 KB 分值: 160  难度:6级算法题
 收藏
 关注

给定一幅无向带权连通图G = (V, E) (这里V是点集,E是边集)。从点u开始的最短路径树是这样一幅图G1 = (V, E1),其中E1是E的子集,并且在G1中,u到所有其它点的最短路径与他在G中是一样的。

现在给定一幅无向带权连通图G和一个点u。你的任务是找出从u开始的最短路径树,并且这个树中所有边的权值之和要最小。


Input
单组测试数据。
第一行有两个整数n和m(1 ≤ n ≤ 3*10^5, 0 ≤ m ≤ 3*10^5),表示点和边的数目。
接下来m行,每行包含3个整数 ui, vi, wi ,表示ui和vi之间有一条权值为wi的无向边(1 ≤ ui,vi ≤ n, 1 ≤ wi ≤ 10^9)。
输入保证图是连通的。
最后一行给出一个整数u (1 ≤ u ≤ n),表示起点。
Output
输出这棵树的最小的权值之和。
Input示例
3 3
1 2 1
2 3 1
1 3 2
3
Output示例
2

题解:求最短路径树,因为最后求出来的是一棵树(n-1条边),所以可以 跑一遍单源最短路,并在此过程中更新改点对应的前驱pre,比如样例中3-->2的最短路就是2,3对应的边,那么2顶点对应的前驱pre[2]=(2,3这条边的权重),3-->1的最短路有两条,(1) 3-->1  (2)3-->2-->1 如果选择第一种,那pre[1]=2(1,3边的权重),选择第二种pre[1]=1(1,2边的权重)。所以在发现两条相同距离的最短路时,应该对pre[v]进行更新,最后的答案就是所有pre[]的总和。找了好久bug,竟然是因为e[]开小了一倍。
#include<iostream>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<stdio.h>
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int Maxn=600005;
const int inf=1e9;
struct Edge{
    int v,w,next;
    Edge(){}
    Edge(int vv,int ww,int nx){
        v=vv,w=ww,next=nx;
    }
}e[Maxn];
int ra,fh;char rx;
inline int read(){
     rx=getchar(),ra=0,fh=1;
     while((rx<'0'||rx>'9')&&rx!='-')rx=getchar();
     if(rx=='-')fh=-1,rx=getchar();
     while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra*fh;
}
int n,m,s,cnt,head[Maxn],pre[Maxn];
bool vis[Maxn];
long long ans,dis[Maxn];
queue<int> q;
void add(int uu,int vv,int ww){
    e[cnt]=Edge(vv,ww,head[uu]);
    head[uu]=cnt++;
}
void spfa(int s){
    for(int i=1;i<=n;i++) dis[i]=INF,pre[i]=inf,vis[i]=0;
    pre[s]=0,dis[s]=0,vis[s]=1;
    q.push(s);
    while(!q.empty()){
        int u=q.front();
        q.pop(),vis[u]=0;
        for(int i=head[u];i>=0;i=e[i].next){
            int v=e[i].v;
            if(dis[v]>dis[u]+e[i].w){
                dis[v]=dis[u]+e[i].w;
                pre[v]=e[i].w;
                if(!vis[v]) q.push(v),vis[v]=1;
            }
            else if(dis[v]==dis[u]+e[i].w&&pre[v]>e[i].w)
                pre[v]=e[i].w;
        }
    }
}
int main(){
    n=read(),m=read();
    ans=0,cnt=0;
    for(int i=1;i<=n;i++)
        head[i]=-1;
    for(int i=0;i<m;i++){
        int u,v,w;
        u=read(),v=read(),w=read();
        add(u,v,w);
        add(v,u,w);
    }
    s=read();
    spfa(s);
    for(int i=1;i<=n;i++) ans+=(long long)pre[i];
    printf("%I64d\n",ans);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值