题目大意:给出n个点,m条无向边,每条边有长度。求一棵树,要求树上的每个点到源点距离最小的前提下,使得树上的边的长度和最小。输出树上边的总长度,以及树上的边的序号(按输入顺序 1...m).
思路 :单源最短路径 + 贪心 .用Dijkstra 或spfa 算法 求每个点到源点的最短路径,并在记录当前用来更新每个点最短距离的那条边(即pre[i]记录点i所对应的变)。
若当前这条边能使得某个点到源点的距离变小则更新该点到源点的距离,并记录更新该点的边; 若当前这条边更新某节点后,使得该点到源点的距离不变(即:d[e[t].to] = d[e[t].from] + e[t].length ) 则比较这条边与当前该点所记录的边的长度大小,若这条边比改点所记录的边长度要小(即e[pre[e[t].to]].length > e[t].length),则更新改点所记录的边(pre[e[t].to] = t)。
代码 :
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<queue>
#define INF 0x7fffffffffffffff
#define pii pair<long long,long long>
using namespace std;
long long s,n,m,pre[300002],tag[600002],u[600002],v[600002],next[600002],first[300002];
long long sum,d[300002],w[600002];
priority_queue<pii,vector<pii>,greater<pii> > Q;
bool vis[300002]; void input(){
long long i,j,a,b,c;
for(i=1;i<=n;i++)
first[i] = -1;
for(i=1;i<=m;i++){
scanf("%I64d %I64d %I64d",&u[i],&v[i],&w[i]);
u[i+m] = v[i] ;
v[i+m] = u[i] ;
w[i+m] = w[i] ;
next[i] = first[u[i]];
first[u[i]] = i ;
next[i+m] = first[v[i]];
first[v[i]] = i+m ;
}
scanf("%I64d",&s);
} void Dijkstra(){
long long i,j;
memset(vis,0,sizeof(vis));
memset(tag,0,sizeof(tag));
for(i=1;i<=n;i++)
pre[i] = -1 ;
for(i=1;i<=n;i++)
d[i] = INF;
d[s] = 0;
Q.push(make_pair(d[s],s));
while(!Q.empty()){
pii t = Q.top();
Q.pop();
long long x = t.second;
if(vis[x])
continue;
vis[x] = true;
for(long long e=first[x]; e!=-1; e=next[e]){
if(d[v[e]] > d[x] + w[e] && d[x] < INF){
pre[v[e]] = e;
d[v[e]] = d[x] + w[e] ;
Q.push(make_pair(d[v[e]],v[e]));}elseif(d[v[e]]== d[x]+ w[e]&& d[x]< INF && w[pre[v[e]]]> w[e]){
pre[v[e]]= e;
Q.push(make_pair(d[v[e]],v[e]));}}}}int main(){longlong i,j;while(scanf("%I64d%I64d",&n,&m)==2){
input();
sum =0;Dijkstra();for(i=1;i<=n;i++){
sum += w[pre[i]];
tag[pre[i]]=1;} printf("%I64d\n",sum);longlong count =0;for(i=1;i<=2*m;i++)if(tag[i]){if(i > m){if(count !=0)
printf(" %I64d",i-m);else
printf("%I64d",i-m);}elseif(i<=m){if(count !=0)
printf(" %I64d",i);else
printf("%I64d",i);}
count ++;}
printf("\n");}return0;}