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