题目链接:http://poj.org/problem?id=1511
嗯,最后一次写SPFA了,以后就套模板了。
题意:给出n个点和n条有向边,求所有点到源点1的来回最短路之和(保证每个点都可以往返源点1).
思路:建反图,从任意一点回来,就变成了反图上的从1到任意一点去。
next记录前驱,head[u],记录u是在第几条边,然后往前扫。例如:edge[0].next = head[1] =0;head[1] = 0;edge[3].next = head[1] =0;head[1] =3;
再扫的时候就是,3->0;
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<vector> #include<queue> using namespace std; #define MAXN 1000100 #define inf 1LL<<60 struct Edge { int v,w,next; } edge1[MAXN*10],edge2[MAXN*10]; int cnt[MAXN]; int head1[MAXN],head2[MAXN]; long long dist[MAXN]; bool mark[MAXN]; int n,m,NE; void add(Edge *edge,int *head,int u,int v,int w) { edge[NE].v=v; edge[NE].w=w; edge[NE].next=head[u]; //边的前驱是head[u],之前的那条边 head[u]=NE; //head[u] 重新覆盖 } long long SPFA(Edge *edge,int *head,int u) { memset(mark,false,sizeof(mark)); memset(cnt,0,sizeof(cnt)); for(int i=1; i<=n; i++) dist[i]=inf; dist[u]=0; mark[u] =true; queue<int>Q; Q.push(u); while(!Q.empty()) { u=Q.front(); Q.pop(); mark[u]=false; for(int i=head[u]; i!=-1; i=edge[i].next) { int v=edge[i].v,w=edge[i].w; if(dist[u]+w<dist[v]) { dist[v]=dist[u]+w; if(!mark[v]) { mark[v]=true; Q.push(v); /*if(++cnt[v]>n) return false;*/ } } } } long long ans=0; for(int i=2; i<=n; i++)ans+=dist[i]; return ans; } int main() { int _case,u,v,w; scanf("%d",&_case); while(_case--) { scanf("%d%d",&n,&m); NE=0; memset(head1,-1,(n+2)*sizeof(int)); memset(head2,-1,(n+2)*sizeof(int)); while(m--) { scanf("%d%d%d",&u,&v,&w); add(edge1,head1,u,v,w); add(edge2,head2,v,u,w);//建反图 NE++; } printf("%lld ",SPFA(edge1,head1,1)+SPFA(edge2,head2,1)); } return 0; }