51 nod 1443 路径和树 【最短路径】

51 nod 1443 路径和树

Description

给定一幅无向带权连通图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

题解:

从rt开始的最短路径树顾名思义就是rt到其他节点的每条最短路径上的所有边构成的树

这题一开始很容易想到,先跑一边最短路,然后再在这些边上最小生成树,但这样比较麻烦,而且容易T掉。
实际上只要在跑SPFA的时候顺便记录下每个点的最小前驱,最后答案求和就行了。

代码如下:
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=3e5+5;
 4 const long long inf=1e18;
 5 int n,m,edge,nxt[2*N],to[2*N],hea[N];
 6 long long val[2*N];
 7 void add(int u,int v,long long w)
 8 {
 9     to[++edge]=v; nxt[edge]=hea[u]; hea[u]=edge; val[edge]=w;
10     to[++edge]=u; nxt[edge]=hea[v]; hea[v]=edge; val[edge]=w;
11 }
12 long long dis[N],pre[N];
13 bool vis[N];
14 void spfa(int rt)
15 {
16     
17     memset(vis,0,sizeof(vis));
18     memset(pre,0,sizeof(pre));
19     for (int i=1; i<=n; i++) dis[i]=inf;
20     int q[3*N];
21     int h=0,t=1;
22     q[t]=rt; vis[rt]=true; dis[rt]=0;
23     
24     while (h<t)
25     {
26         int x=q[++h]; vis[x]=false;
27         for (int i=hea[x]; i; i=nxt[i])
28         {
29             if (dis[to[i]]>dis[x]+val[i])
30             {
31                 dis[to[i]]=dis[x]+val[i];
32                 pre[to[i]]=val[i];
33                 if (!vis[to[i]])
34                 {
35                     vis[to[i]]=true; q[++t]=to[i];
36                 }
37             }
38             else if (dis[to[i]]==dis[x]+val[i]) pre[to[i]]=min(pre[to[i]],val[i]);
39         }
40     }
41 }
42 int main()
43 {
44     scanf("%d%d",&n,&m);
45     for (int i=1; i<=m; i++)
46     {
47         int u,v; long long w;
48         scanf("%d%d%lld",&u,&v,&w);
49         add(u,v,w);
50     }
51     int s;
52     scanf("%d",&s);
53     //cout<<"YES"<<endl;
54     spfa(s);
55     long long ans=0;
56     for (int i=1; i<=n; i++)
57       ans+=pre[i];
58     printf("%lld
",ans);
59     return 0;
60 }
View Code



加油加油加油!!! fighting fighting fighting !!!

 
原文地址:https://www.cnblogs.com/Frank-King/p/9298629.html