原题链接
思路:
答案包含两部分
1.从1到其它所有点i的最短距离的和。
2.从其它所有点i到1的最短距离的和。
注意是单向边。
第一反应是用floyed算法但是1e3的数据量明显超时,最后我试了一下只有四十分。
看了别人的题解知道了还有建反向图这种操作,我在这详细说明一下。建反向图其实就是:
比如d[i][j]代表从i到j的距离,反向的话就是把d[i][j]和d[j][i]互换过来。
原理是这样的:
从u到v距离是w,那么从v到u距离也是w,单纯把方向反过来。说的话感觉总是说不到那个点子上,实在不行就画个图瞅瞅。
然后跑两遍dijkstra就行了。
代码:
#include<iostream>
#include<cmath>
#include<vector>
#include<string>
#include<fstream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e3+10,M=1e5+10;
struct edge
{
int to,next,w;
};
struct node
{
int to,w;
node(int u=0,int ww=0):to(u),w(ww){}//构造函数
bool operator<(const node&a)const
{
return w>a.w;
}
};
edge e1[M],e2[M];
int head1[N],head2[N],cnt=0,n,m;
int dis_1[N],dis_2[N];
bool done_1[N],done_2[N];
void addedge(int u,int v,int w)
{
e1[++cnt].to=v;
e1[cnt].w=w;
e1[cnt].next=head1[u];
head1[u]=cnt;
e2[cnt].to=u;
e2[cnt].next=head2[v];
e2[cnt].w=w;
head2[v]=cnt;
}
void dijkstra_1()//正向图
{
for(int i=1;i<=n;i++) dis_1[i]=inf,done_1[i]=false;
dis_1[1]=0;
priority_queue<node>q;
q.push(node(1,0));
while(!q.empty())
{
node s=q.top();
q.pop();
if(done_1[s.to]) continue;
done_1[s.to]=1;
for(int i=head1[s.to];i;i=e1[i].next)
{
if(dis_1[s.to]+e1[i].w<dis_1[e1[i].to])
{
dis_1[e1[i].to]=dis_1[s.to]+e1[i].w;
q.push(node(e1[i].to,dis_1[e1[i].to]));
}
}
}
}
void dijkstra_2()//反向图
{
for(int i=1;i<=n;i++) dis_2[i]=inf,done_2[i]=false;
dis_2[1]=0;
priority_queue<node>q;
q.push(node(1,0));
while(!q.empty())
{
node s=q.top();
q.pop();
if(done_2[s.to]) continue;
done_2[s.to]=1;
for(int i=head2[s.to];i;i=e2[i].next)
{
if(dis_2[s.to]+e2[i].w<dis_2[e2[i].to])
{
dis_2[e2[i].to]=dis_2[s.to]+e2[i].w;
q.push(node(e2[i].to,dis_2[e2[i].to]));
}
}
}
}
int main()
{
int u,v,w;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
}
int ans=0;
dijkstra_1();
dijkstra_2();
for(int i=2;i<=n;i++) ans+=dis_1[i]+dis_2[i];
printf("%d\n",ans);
return 0;
}