2018 年 7 月 19 日,某位同学在 NOI Day 1 T1 归程 一题里非常熟练地使用了一个广为人知的算法求最短路。
然后呢?
100→60100 \rightarrow 60100→60 ;
Ag→CuAg \rightarrow CuAg→Cu ;
最终,他因此没能与理想的大学达成契约。
小 F 衷心祝愿大家不再重蹈覆辙。
题目描述
给定一个 NNN 个点, MMM 条有向边的带非负权图,请你计算从 SSS 出发,到每个点的距离。
数据保证你能从 SSS 出发到任意点。
输入输出格式
输入格式:
第一行为三个正整数 N,M,SN, M, SN,M,S 。 第二行起 MMM 行,每行三个非负整数 ui,vi,wiu_i, v_i, w_iui,vi,wi ,表示从 uiu_iui 到 viv_ivi 有一条权值为 wiw_iwi 的边。
输出格式:
输出一行 NNN 个空格分隔的非负整数,表示 SSS 到每个点的距离。
输入输出样例
输入样例#1: 复制
4 6 1 1 2 2 2 3 2 2 4 1 1 3 5 3 4 3 1 4 4
输出样例#1: 复制
0 2 4 3
说明
样例解释请参考 数据随机的模板题。
1≤N≤1000001 \leq N \leq 1000001≤N≤100000 ;
1≤M≤2000001 \leq M \leq 2000001≤M≤200000 ;
S=1S = 1S=1 ;
1≤ui,vi≤N1 \leq u_i, v_i\leq N1≤ui,vi≤N ;
0≤wi≤1090 \leq w_i \leq 10 ^ 90≤wi≤109 ,
0≤∑wi≤1090 \leq \sum w_i \leq 10 ^ 90≤∑wi≤109 。
本题数据可能会持续更新,但不会重测,望周知。
1.就是dj用优先队列优化(结构体的优先队列还是应该多写啊,改了好多次orz)
2.然后用优先队列有一个大坑:
重点重点:一般的dj是松弛n次就好,但是用了优先队列一个点可能会出现多次,(比如你13,使得dist[3]减小入队,后面可能23又一次入队,那这样3可能两次在q.top)所以循环应该是while(!q.empty()),再加上一个if(dick.w!=dist[mid])continue的优化就可以了.
ac代码:
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
long int n,m,s,mid,mid1,dist[100050],head1[100050];
struct data
{
long int head,to,w;
};
bool operator <(data a,data b)
{
return a.w>b.w;
}
data dick,edge[200050];
priority_queue <data> q;
void init()
{
scanf("%ld%ld%ld",&n,&m,&s);
for(int i=1;i<=m;i++)
{
scanf("%ld%ld%ld",&mid,&edge[i].to,&edge[i].w);
edge[i].head=head1[mid];
head1[mid]=i;
}
}
void dis()
{
for(int i=1;i<=n;i++) dist[i]=0x7fffffff;
dist[s]=0;dick.w=0;dick.head=s;q.push(dick);
while(!q.empty())
{
dick=q.top();q.pop();
//cout<<dick.head<<endl;
mid=dick.head;mid1=head1[mid];
if(dick.w!=dist[mid]) continue;
while(mid1)
{
if(dist[edge[mid1].to]>dist[mid]+edge[mid1].w)
{
dist[edge[mid1].to]=dist[mid]+edge[mid1].w;
dick.head=edge[mid1].to;dick.w=dist[edge[mid1].to];
q.push(dick);
}
mid1=edge[mid1].head;
}
}
}
int main()
{
init();
dis();
for(int i=1;i<=n;i++) printf("%ld ",dist[i]);
}