51nod赛艇表演
题目简要描述:
有 \(n\) 个节点 \(m\) 条边的无向图,每条边有边权,每个点有点权。从一个点出发到另一个点去看赛艇表演,所要花费的代价是往返道路的边权和加上赛艇表演的节点点权。输出从每一个节点出发看赛艇表演的最小代价。(注:可以不动,就在这个节点看)n,m<=2e5,点权,边权<=1e12
【样例】
samplein:
4 2
1 2 4
2 3 7
6 20 1 25
sampleout:
6 14 1 25
这道题很精彩,他用到了巧妙建图。容易想到的暴力算法是针对每一个点求一个最短路,此时注意因为是往返所以每条边权要乘二,另外加上终点的点权,取最小值,就是一个答案。
我们考虑在这 n 个点外建一个节点 0,在 0 和 1~n 间都连一条边。我们的思路是把“【每个节点】→【使得代价最小的节点】”转化成“0号点(源点)到【每个节点】的最短路”。这样一来 0 到 1~n 的边权就应该分别是 1~n 的点权。于是求一遍单源最短路(SPFA)就好了。
总结:对于最短路性图论想要优化复杂度可以想一想建立源点、汇点;善于逆向思考,把多对一、多对多转化成一对多甚至一对一。
#include <bits/stdc++.h>
#define pb push_back
#define int long long
using namespace std;
const int N=2e5+5,inf=1e17;
int a[N],dis[N],book[N];
struct type { int node,edge; } t;
queue<int> Q;
vector<type> G[N];
signed main()
{
int n,m,u,v,w;
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>u>>v>>w;
t.node=v,t.edge=w*2;
G[u].pb(t);
t.node=u,t.edge=w*2;
G[v].pb(t);
}
for(int i=1;i<=n;i++){
cin>>a[i];
t.node=i,t.edge=a[i];
G[0].pb(t);
t.node=0,t.edge=a[i];
G[i].pb(t);
dis[i]=inf;
}
Q.push(0),book[0]=1;
while(!Q.empty()){
int now=Q.front();
Q.pop(),book[now]=0;
for(int i=0;i<G[now].size();i++)
if(dis[G[now][i].node]>dis[now]+G[now][i].edge){
book[G[now][i].node]=1;
Q.push(G[now][i].node);
dis[G[now][i].node]=dis[now]+G[now][i].edge;
}
}
for(int i=1;i<=n;i++) cout<<dis[i]<<' ';
return 0;
}