写单源最短路弱化版的时候,就已经快累死了,再来个标准版,一样的题,这数据妥妥天差地别QAQ。先把题复制过来:
题目背景
2018 年 7 月 19 日,某位同学在 NOI Day 1 T1 归程 一题里非常熟练地使用了一个广为人知的算法求最短路。
然后呢?
100→60100→60;
Ag→CuAg→Cu;
最终,他因此没能与理想的大学达成契约。
小 F 衷心祝愿大家不再重蹈覆辙。
题目描述
给定一个 �n 个点,�m 条有向边的带非负权图,请你计算从 �s 出发,到每个点的距离。
数据保证你能从 �s 出发到任意点。
输入格式
第一行为三个正整数 �,�,�n,m,s。 第二行起 �m 行,每行三个非负整数 ��,��,��ui,vi,wi,表示从 ��ui 到 ��vi 有一条权值为 ��wi 的有向边。
输出格式
输出一行 �n 个空格分隔的非负整数,表示 �s 到每个点的距离。
输入输出样例
输入 #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≤�≤1051≤n≤105;
1≤�≤2×1051≤m≤2×105;
�=1s=1;
1≤��,��≤�1≤ui,vi≤n;
0≤��≤1090≤wi≤109,
0≤∑��≤1090≤∑wi≤109。
本题数据可能会持续更新,但不会重测,望周知。
2018.09.04 数据更新 from @zzq
还好昨天学长讲了优先队列,不然一直TLE~__~!!!好吧,先上代码:
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
int head[100001],cnt;
long long ans[1000000];//预防相加起来太大超过int
bool vis[1000000];
int m,n,s;
struct Edge
{
int to;
int next;
int w;
}edge[1000000];
struct priority
{
int ans;
int id;
bool operator <(const priority &x)const//用取地址符不需要重新开辟空间
{
return x.ans<ans;
}
};
void add(int x,int y,int z)
{
edge[++cnt].to=y;
edge[cnt].w=z;
edge[cnt].next=head[x];
head[x]=cnt;
}
priority_queue<priority> q;
int main()
{
scanf("%d %d %d",&n,&m,&s);
for(int i=1;i<=m;i++)
{
ans[i]=2147483647;
}
ans[s]=0;
for(int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
add(a,b,c);
}
int u;
q.push((priority){0,s});//这里插入了第一个元素id=s,ans=0;
while(!q.empty())
{
priority temp=q.top();
q.pop();
u=temp.id;
if(!vis[u])
{
vis[u]=1;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(ans[v]>ans[u]+edge[i].w)
{
ans[v]=ans[u]+edge[i].w;
if(!vis[v])
{
q.push((priority){ans[v],v});
}
}
}
}
}
for(int i=1;i<=n;i++)
{
printf("%lld ",ans[i]);
}
}
在解释代码前,来了解一下这个代码里面的有关新知识:
首先是#include<iostream>头文件,好像是每个c++必备用于这个cin和cout的输入输出,而且要带上这么一个东西:using namespace std; 而这个cstdio.h就是标准c++,用都也要加上using namespace std因为这里面的函数都是定义在一个名称空间std里面的;至于#include<queue>这个就是用优先队列的头文件。
对了说到这个,为什么这里还可以用printf和scanf,我百度了一下:
在C++下,若要使用C中已有库中的函数如stdio,文件包含方式为前面加一个c,同时去掉.h后缀
这里要注意,虽然printf和scanf的输入形式比cin和cout麻烦不少,但是他们速度快呀。
struct priority
{
int ans;
int id;
bool operator <(const priority &x)const//用取地址符不需要重新开辟空间
{
return x.ans<ans;
}
};
这里本来定义了一个结构体,但是后面又加了一个
priority_queue<priority> q;
声明了了一个优先队列,而类型就是我们刚刚定义的结构体类型,队列名叫q,而优先队列之所以神奇就在于它可以自动排序,其实它是从大到小排序的,不对,应该说是在声明优先队列的时候,<>里面就只一个变量的话,默认排序是从大到小的,其实他后面省略了两个变量,这个声明原来的样子本来是这样的,prioirity_queue<priority,vector<priority>,less<priority> >中间这个是容器,表示用一个什么东西装,这里一般都不做大修改,而后面那个就是规则,比如这里less的规则就是小于,也就是按从大到小的规则排序,也就是我所说的默认排序,而如果想改成从小到大的话,可以把less改成greater,当然,我说这样改是指一般类型,像我们这里的这个要改的话,不仅要改这个,还要将那个规则上的operator <改成operator >,当然还有一个更好的办法,就是直接改operator下面括号里的<改成>
这个变量里面由于也带有<>所以一定要小心,在最后一个变量会出现>>这时候一定要把它从中间用空格隔开不然就成平移符号了
bool operator <(const priority &x)const//用取地址符不需要重新开辟空间
{
return x.ans<ans;
}
而结构体里面的这个就相当于在定规则,为什么还要定规则呢,那是因为结构体里面有多个元素,到底是根据哪个元素排,要定义规则,所以我们来浅浅解读一下这坨意思,意思是:如果结构体priority里面的ans大,那就结构体priority小,也就是不优先,意思就是说,ans小的结构体越优先越在前面;
在优先队列中有这么几种:
以队列q为例:
q.pop()输出堆顶;
q.push()插入元素;
q.top()返回堆顶;
q.empty()表示队列是否空了,空了即返回1,否则为0;
感觉有点缺斤少两,具体思想就和弱化版差不多。
(13条消息) 2.1学习总结_穿花云烛展的博客-CSDN博客。
但是还是再清楚一点显得我的好态度:
while(!q.empty())
{
priority temp=q.top();
q.pop();
u=temp.id;
if(!vis[u])
{
vis[u]=1;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(ans[v]>ans[u]+edge[i].w)
{
ans[v]=ans[u]+edge[i].w;
if(!vis[v])
{
q.push((priority){ans[v],v});
}
}
}
}
}
来浅浅解释一下这一段优先队列用的最多的代码吧!先是在队列不为空的情况下,让一个临时变量来存下堆顶,并且先将它输出,为什么是堆顶呢,因为其实你会发现它的起点元素就在堆顶,不管循环多少次,然后再让u=temp.id,后面大概找到一个小的就存一个。思想详见(13条消息) 2.1学习总结_穿花云烛展的博客-CSDN博客
然后还是一碗鸡汤:太深的流连便成了一种羁绊,绊住的不仅是双脚,还有未来。