UVA721 Invitation Cards(SPFA,反向建图技巧)
UVA721 Invitation Cards
反向建图是一种非常重要的解题思想,务必掌握.
题目描述
给定一张图,求从源点1到每个点的最短路之和和每个点到源点1的最短路之和的和. 保证整张图强连通,答案在
题目分析
求源点1到每个点的最短路之和的做法是显然的.
而求每个点到源点的最短路,最直观的思路是直接每个点进行一次求最短路,但是观察数据范围,发现数据是百万级的,明显时间复杂度不满足.
我们考虑每个点到源点1的最短路. 可以发现,就算我们对每个点进行一次求最短路,实际上也只有到源点的数据派的上用场. 反过来想,源点到每个点的数据都是有用的,那么我们考虑从源点再做一次最短路. 由于之前的终点—源点变成了起点,所以要反向建图. 这样再跑一次最短路,求得的
考虑使用什么算法. 观察最极端的数据,发现
程序实现
//小写字母表示原图,大写字母表示反图
#include<bits/stdc++.h>
#define maxn 1000010
#define inf 0x3f3f3f3f
using namespace std;
struct edge{
int v,w,next;
}e[maxn],E[maxn];
int head[maxn],tot1;
void add(int u,int v,int w){
e[++tot1].v =v;
e[tot1].w =w;
e[tot1].next =head[u];
head[u]=tot1;
}
int Head[maxn],tot2;
void Add(int u,int v,int w){
E[++tot2].v =v;
E[tot2].w =w;
E[tot2].next =Head[u];
Head[u]=tot2;
}
int dis[maxn];
bool vis[maxn];
void spfa(){
memset(dis,inf,sizeof dis);
memset(vis,false ,sizeof vis);
dis[1]=0;
vis[1]=true;
queue<int >q;
q.push(1);
while(!q.empty()){
int u=q.front();
for(int i=head[u];i;i=e[i].next ){
int v=e[i].v ;
if(dis[v]>dis[u]+e[i].w ){
dis[v]=dis[u]+e[i].w ;
if(!vis[v]){
vis[v]=true;
q.push(v);
}
}
}
vis[u]=false;
q.pop();
}
}
int Dis[maxn];
void Spfa(){
memset(Dis,inf,sizeof Dis);
memset(vis,false,sizeof vis);
Dis[1]=0;
vis[1]=true;
queue<int >q;
q.push(1);
while(!q.empty()){
int u=q.front();
for(int i=Head[u];i;i=E[i].next ){
int v=E[i].v ;
if(Dis[v]>Dis[u]+E[i].w ){
Dis[v]=Dis[u]+E[i].w ;
if(!vis[v]){
vis[v]=true;
q.push(v);
}
}
}
vis[u]=false;
q.pop();
}
}
int n,m,ans;
int main(){
int T;
scanf("%d",&T);
for(int ab=1;ab<=T;ab++){
memset(e,0,sizeof e);
memset(head,0,sizeof head);
memset(E,0,sizeof E);
memset(Head,0,sizeof Head);
ans=0;//初始化要彻底,不要残留数据
scanf("%d%d",&n,&m);
for(int i=1,u,v,w;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
Add(v,u,w);
}
spfa();
Spfa();
for(int i=1;i<=n;i++){
ans+=(dis[i]+Dis[i]);
}
printf("%d\n",ans);
}
return 0;
}