[CF938D] Buy a Ticket
题目
解说
先说一下,洛谷上的中文翻译是有问题的,路程是往返的,汉语翻译没体现这一点,看一下原题里面的公式就行了。
这道题和之前我自己出的02的爱恋有些许相似,都需要把点权转化为边权。怎么转化呢?由于只有在终点处需要加上点权,所以我们自然地想到超级源点,从超级源点到每个点都连一条权值为点权的单向边保证只走一次,之后跑一次超级源点的单元最短路就行了。当然这就暗含了另一种思维方法:逆向。题目原意是只加终点的点权,但由于道路双向,我们从终点开始跑也是一样的,因此可以把终点当做起点跑最短路。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=200000+5;
int n,m,tot;
int head[maxn];
ll dis[maxn];
struct edge{
int to,next;
ll w;
}e[3*maxn];
struct node{
int num;ll dis;
node(int x,ll y){
num=x;
dis=y;
}
bool operator<(const node &a)const{
return dis>a.dis;
}
};
void add(int a,int b,ll w){
e[tot].to=b;
e[tot].w=w;
e[tot].next=head[a];
head[a]=tot;
tot++;
}
void Dijs(){
priority_queue<node> q;
bool f[maxn];
memset(f,0,sizeof(f));
for (int i=1;i<=n;i++) dis[i]=4611686018427387903;
dis[0]=0;
q.push(node(0,0));
while(!q.empty()){
node p=q.top();q.pop();
int k=p.num;
if(f[k]) continue;
f[k]=1;
for (int i=head[k];i;i=e[i].next){
int u=e[i].to;ll len;
len=dis[k]+e[i].w;
if(dis[u]>len){
dis[u]=len;
q.push(node(u,len));
}
}
}
}
int main(){
tot=1;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v; ll w;
scanf("%d%d%lld",&u,&v,&w);
add(u,v,2*w);//每条边要走两遍
add(v,u,2*w);
}
for(int i=1;i<=n;i++){
ll w;
scanf("%lld",&w);
add(0,i,w);//超级源点编号为0
}
Dijs();
for(int i=1;i<=n;i++) printf("%lld ",dis[i]);
return 0;
}
幸甚至哉,歌以咏志。
签名:
我将轻轻叹息,叙述这一切,
许多许多年以后:
林子里有两条路,我——
选择了行人稀少的那一条,
它改变了我的一生。