题解 P1629 【邮递员送信】
核心思路:
Dijkstra + 链式前向星 + 堆优化的题解。
这一题明显是求最短路。与模板不同,它要求的是多源最短路,是每个节点到源点的距离。
这里如果用暴力,或者每个节点求一遍,显然在时间上是过不去的。(每个节点求一遍的堆优化的Dijkstra是50分)
所以,就要用一个重要的结论:逆图中源点到各个点的最短路就是原图中各个点到源点的最短路。
理解过程:
我们可以想象一下,在原图中,n --> 1的最短路,如果在反向图中不就是 n <-- 1 吗?
有了这个结论,这道题就简单多了:先求一个原图的最短路,再求一个逆图的最短路。这题就结束了。
附上代码:
#include <bits/stdc++.h>
#define MAXN 100005
#define INF 0x3f3f3f3f
struct EDGE //链式前向星
{
int to,nxt,val;
} e[MAXN],ne[MAXN]; //原图与逆图
struct node //写一个堆的结构体
{
int d,pos;
bool operator <( const node &x ) const {return x.d<d;}
};
int cnt=0,adj[MAXN],nadj[MAXN];
int dis[MAXN]; bool vis[MAXN];
int ori=1,n,m;
int ans=0;
std::priority_queue < node > q; //优先队列的堆优化
void addedge(int u,int v,int w) //存图
{
++cnt;
e[cnt].to=v; e[cnt].val=w; e[cnt].nxt=adj[u]; adj[u]=cnt;
ne[cnt].to=u; ne[cnt].val=w; ne[cnt].nxt=nadj[v]; nadj[v]=cnt;
}
void init() //初始化
{
for(int i=1;i<=n;++i) dis[i]=INF;
std::memset(vis,0,sizeof(vis));
while(!q.empty()) q.pop();
}
void Dijkstra(EDGE e[],int adj[]) //函数变量中记得带上邻接域
{
init(); //千万记得初始化
dis[ori]=0; q.push( (node) {0,ori});
while(!q.empty())
{
node temp=q.top(); q.pop();
int u=temp.pos;
if(vis[u]) continue;
vis[u]=1;
for(int i=adj[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(dis[v]>dis[u]+e[i].val)
{
dis[v]=dis[u]+e[i].val;
if(!vis[v]) q.push( (node) {dis[v],v});
}
}
}
}
int main()
{
std::scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)
{
int u,v,w; std::scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
}
Dijkstra(e,adj);
for(int i=1;i<=n;++i) ans+=dis[i];
Dijkstra(ne,nadj);
for(int i=1;i<=n;++i) ans+=dis[i];
std::printf("%d",ans);
return 0;
}