C语言求问!跪求大佬怎么进p站各位大佬救命,这道题怎么做啊,实在太难了orz

考研英语真的很难么? - 知乎<strong class="NumberBoard-itemValue" title="被浏览<strong class="NumberBoard-itemValue" title="8,141分享邀请回答66276 条评论分享收藏感谢收起原文答案
先说结论:考研是选拔性考试,而考研英语是这场选拔性考试中的拔高性科目,所以难是肯定的,但“难”不代表你不能得高分。下文将具体介绍我的考研英语全程备考经历,请考研人纳之:个人介绍及成绩截图
我是大郎神,我为自己代言!行文伊始,大郎神先做下自我介绍:某师范大学毕业,本一,但非211、985;本科所学专业为经管类,硕士报考京津地区985,现已被录取;四六级均过,但分算不上高。今年硕考科目为英语一(201),成绩84分,未报任何辅导班。为了大家能在暑假备考期汲取所需,我特将自己在英语方面的心得拿出来与大家分享,非喜勿喷咯。有想了解考研政治与数学如何学习的,请自行移步郎神高赞文章 与 !正文分享
大郎神属于极度社交活跃型学渣,还未决定考研之时,就在朋友圈频繁目睹学霸师兄由于英语不堪造成的考研悲剧。因此,我在天然属性上便更加重视英语,准确的说这是一种油然而生的征服欲。八月份下定决心考研,满打满算5个月的复习时间。由于自己是跨考,专业书籍繁多,仅推荐书目就有10本,所以在时间分配上就必须要求我把大把大把的时间放在专业课的复习上。除去专业课,还有政治、英语,虽然很多过来人都说政治不着急,到11月你再集中发力就可以。但当你在一眼书没看、知识点完全懵逼的状态下,你一定觉得师兄师姐都是站着说话不腰疼。当时的我一心笃定:得专业课者得天下,只有在专业课上形成碾压之势才能拿下初试;老子英语平时还可以,可以把英语复习时间匀出来些整专业课和政治。后来的实践,证明自己还是太年轻。八月入手一本新东方乱序、一本张剑150篇阅读模拟,就开始了我的英语复习。说实话,做模拟题的日子,我的脑袋完全处在懵逼的状态。一篇阅读拿到手里,拾掇拾掇没几个认识的单词,一天4篇阅读20个题,往往只能对7,8个。每晚回去背新东方词汇,一共5000多个核心词汇,往往都是背了后面的忘了前面的。望着隔壁班的学霸都在蹭蹭的赶进度,心焦之情难免顿生,于是乎“卧槽”便成了我在做英语时的口头禅。毋庸置疑,人在绝望时迸发出的能量往往是巨大的,有时自己都不敢相信。我在保证自己每天专业课进度正常的基础上,开始向旁边的看起来很牛逼的英语茬请教,开始去反思自己的学习方法。通过与他们的沟通,我认清了这样几个事实:1.我很努力,旁边的人也很努力,甚至比我还努力,在现有的学习强度下按照现有的方法(即背词汇+做模拟+最后刷真题)来学习,我再学一年也不可能把基础本来就比我好的人超越;2.我的目的是为了考高分,而不是为了增长那些暂时用不上的英语知识,所以做对题比任何事都重要;3.学习时间长、笔记多、做题多不一定证明自己学得好,手勤快不如脑袋勤快,所以能用思考解决的问题不要用笔,除非必须。最终,我在9月改变了自己的方法,不再专门背诵词汇书,抛弃了张剑阅读模拟题,买了年的所有真题,开始狂刷真题中的阅读,以阅读来带动词汇、答题逻辑、以及翻译、作文的能力培养。▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂具体的刷题方式是这样的,将真题分为两大部分—部分和部分,第一部分文章较短较简单,第二部分文章较长,且难度较大。我从第一部分开始做起,刚开始一天做两篇(A+D或B+C的难度组合),两天就能做一套题,核心点在于:1.做题时不在题目上做任何勾画,将答案写于一张草稿纸上即可,便于习题可重复练习且在后期作答时没有任何答案信号;2.一篇阅读时间严格控制在15分钟之内,即使不确定答案选项也需每次强迫自己在规定的时间内把答案填充上去,做年真题时该要求较易实现,但在做年真题时较难,所以必须克服;3.每日做完两篇阅读后,立马对答案,无论结果如何,先抛开不看,再翻过头来全文翻译这两篇文章,在翻译过程中把自己不会的单词、长难句在草稿纸上进行简单记录并在辅导书中寻求答案,保证在不借助任何外力的情况下,能将文章从头至尾口译到底;4.每天晚上抽出复习的最后40分钟,把每天做的阅读再从头到尾翻译一遍,是要用更流利的速度,再简单回顾下自己当时做本篇阅读时选错的题目,把所有选项都一一排除;5.按照一天两篇阅读的速度,完完全全可以在20天的时间内把的阅读题型做完,做完此部分后,再按倒序从2005年开始翻回1994年,一天可做2篇阅读+一篇本年的翻译(同法),保证在此遍过程中少错题,翻译更加流畅。同理,加快速度,将这10年的完形带入该过程,你会发现在一个半月后,你的词汇量得到了很大提升、做题速度也能满足15分钟甚至每篇10分钟的要求,关键是做题时的思维能力越来越和出题者并轨;6.在做完第一部分的基础上,开始以同样的方法刷第二部分,限定时间仍旧不变,此部分更加注重自己的即时消化能力,控制笔记单词的数量,能背不记,同样保证刷本部分3遍左右。在按照上法进行到11月中旬的时候,我发现自己的词汇、阅读、翻译能力有了明显质的提升。长时间处在真刀真枪干的过程中,信心也随着正确率的提高逐渐树立起来。来到这个节点,需要给各位指明的是,在刷题过程中,随着做题遍数的增加,正确率必然会增加,一方面确实是由于自己能力的提高,另一方面也可能是自己对文章、题型的熟悉度大大增加。举个例子,同样一道选proper title的题,你第一次做错,但当你在做第二遍、第三遍的时候,正确答案就会在你心中呼唤,影响你的决策,因此,在刷题过程中,做题已不是关键,读懂全文、理顺全文翻译、从作者角度去架构题目设计便变成了重中之重。▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂从11月下半旬开始,我开始每天练习一整套真题,具体来说,我会按照考试标准,抽一个自己舒服的大块时间(大约为3个小时)来集中做题(不受任何人干扰,全程在空白纸上按照考试标准规范作答,仍旧不在真题上勾画),做题顺序为:4篇阅读(60分钟)、翻译(20分钟)、小作文(10分钟)、大作文(20分钟)、新题型(15分钟)、完形(15分钟),全程大致为2小时20分钟。好多同学看到这里,可能会说,我现在一篇阅读做到20分钟都会错很多,更何况15分钟呢?大郎神当年也有同样的疑惑,可是越到后期越发现,在我前期练习大量真题的基础上,做整套真题熟悉度特别高,做题的速度也超乎寻常的快,我往往在1个半小时之内就能把所有的题型都做完(其中大部分的时间都砸在了大作文和新题型之上)。剩余的一个半小时,我会用来整理大量的适用于自身的英文模板、名言佳句、真题名句改编以及思考反复练习却反复出错的真题。放心,这一个半个小时已足够完成这些工作。我从11月中旬到12月10号左右,每天必做一套真题,在坚持走完一遍真题后,除去大小作文、翻译等主观题,我的客观题正确率已将近到达100%,而我每天分配给英语的时间不会超过3个小时。此时距考试还有不到20天时间,考虑到政治、专业课背诵任务极重,而真题又太熟悉了,我又果断开始了自己第三个阶段的冲刺阶段——买一套张剑最后冲刺5套卷以及某某作者任意一本小薄本的作文预测。买预测题是为了锻炼自己应对陌生题型的适应性,买作文是为了摘录名言佳句以及某类话题的必备用词(例如:文化火锅的话题用词),预测题往往不具预测性,但可以作为甄别你临场心态的PH试纸。在单数天,我会做一套预测题+总结(一般会在3个小时或稍多,毕竟是新题);在双数天,会按照考试要求做一套真题(2个小时足够)+大小作文模板及词汇语句的背诵1个小时。这样来回对自己进行新旧试题的双重打磨,可以保证自己在12月20号左右将这5套模拟题做完。剩下的一周时间,做两套真题重拾信心,主要精力用在观预测作文以及大小作文背诵格式及话题词的背诵上,每日复习时间控制在2个小时左右即可。大郎神在12月26日下午进行英语考试,临场前狂风大作、砂石乱舞,使本旧糟糕的心情更加疏离。发下试卷后,浏览大致便开始疯狂开写。由于很害怕折戟英语场,所以在开场之后态度很严肃,速度也比平时稍快些,再加上今年新题型较为简单,所以客观题部分答题很快(具体为:4篇阅读50分钟+翻译和新题型25分钟)。令人想不到的是,由于客观部分较为顺利,心态有所放松,直接导致后面的大作文磨磨唧唧写了将近50分钟,牛逼句型、谚语倒用了不少。但由于平时放松对通知类小作文的书写,所以一看到Notice就懵逼了,一下手就整了句,Dear XXX开头,改还改不了,当时和吃了屎一样,说实话心态有点慌。格式就不用说了,肯定糟糕的很,内容也将就切题,花了20分钟写完小作文,我的内心是崩溃的,看着窗外尘土漫天不禁忧心忡忡。最后花了20分钟时间把完形彻彻底底做了一遍。完事等待交卷。当天走出考场,一心抱怨上天不公平,丫的平时速度练那么快,题做那么多,居然还有意外情况出现。抱着这种吃了屎的心情,一直挨到出分那天。由于天津是最晚出分的省份,所以在我查分时都有N多同学在炫耀他们考得如何如何,并且向我投来了轻蔑的问候(哈哈哈哈哈,开玩笑滴),同学英语大部分高分集中在(72-76间)。说实在的,大郎神心里有点犯嘀咕,心想都丫的考这么高,今年肯定名落孙山了。然并卵,等待我成绩界面弹出的刹那间,心里瞬间划过一丝希冀,老子英语居然考了84 。后来找到官方答案与记忆一对比,发现客观题只错了1个阅读+6个完形(共5分)、再预估翻译扣2分、小作文扣6分、大作文扣3分。至此,大郎神的考研英语算是到此结束。▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂码字超累,为了能写出更多的考研干货文章,请学弟学妹先点赞再收藏!后期根据点赞数定期更新各科目的具体规划咯!有想要咨询考研备考的孩子,可加大郎神个人微信:D,记得注明“知乎考研”!更多考研干货,尽在公众号“大郎神考研”!为了方便浏览,特将知乎考研高赞回答置于下方,请根据需求自行观看:﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋1.﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋2.﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋3.﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋4.﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋﹋5.▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂▂﹏▂﹏▂﹏▂﹏▂﹏▂﹏▂28745 条评论分享收藏感谢收起先锋游戏知道信息频道欢迎您
编程:求C语言大神帮忙~大神们,我又有问题了orz...
[欢乐猪] [ 14:32:48] (<span id="tgd) (<span id="tfd) &&
最佳答案程序编译没问题。。。。。。。。。。。。。。。。。。。
大神救救我,我就是上个礼拜那个人
#include&&int main(){int i,j;for (i=1;i&11;i++){for (j=1;j&=i;j++){printf(&*&);}printf(&\n&);}for (i=1;i&11;i++){for (j=10;j&=i;j--){printf(&*&);}printf(&\n&);}}
可是我要怎么把那些星号置中???
每次加一个星号置中无法左右对齐 不好看
那我要怎么在那下面追加上图的星号?
#include&&int main(){int i,j,k,l;for (i=0;i&10;i++){for (k=0;k&i;k++){printf(& &);}for (j=10;j&i;j--){printf(&*&);}printf(&\n&);}& & k=1;for (i=0;i&5;i++){for (l=0;l&9-k;l++){printf(& &);}for (j=0;j&k;j++){printf(&*&);}& & & & & & k+=2;printf(&\n&);}}
昵称: 验证码:
评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述
编程相关知识
编程其他问题信道与编码若干实验题(要求用C语言编写)~求高手
var sogou_ad_id=731549;
var sogou_ad_height=160;
var sogou_ad_width=690;题解 P3371
最近编辑时间:
以下题解仅供学习参考使用。
抄袭、复制题解,以达到刷AC率/AC数量或其他目的的行为,在洛谷是严格禁止的。
洛谷非常重视学术诚信。此类行为将会导致您成为作弊者。
具体细则请查看。
这里介绍一种线段树优化Dijkstar。
Dijkstra要查询当前点中dis的最小值可以用堆优化,似乎线段树也可以。
线段树储存点的编号,每取一个点,把它的值改成INF表示这个点已经访问过,其他的点直接在树上修改就好,每次查询整个线段树的最小值即可。复杂度nlog(n),用zkw线段树,常数也不算大,写的比较丑240ms
#include&cstdio&
#include&cstring&
#include&algorithm&
const int maxn =10007;
const int maxm = 500007;
const int INF = 0x7
inline int read() {
char c=getchar();
while(c&'0'||c&'9')
c=getchar();
while(c&='0'&&c&='9') x=x*10+c-'0',c=getchar();
struct node{
int v,next,w;
}edge[maxm];
int num=0,head[maxn];
inline void add_edge(int a,int b,int c) {
edge[++num].v=b;edge[num].w=c;edge[num].next=head[a];head[a]=
int dis[maxn],ans[maxn],s,t;
int tree[maxn&&2],
inline int check(int i,int j) {
return dis[i]&dis[j]?i:j;
inline void build() {
std::memset(dis,0x3f,sizeof dis);// for(int i=0;i&=n+1;i++) dis[i]=INF;
for(leaf=1;leaf&=n;leaf&&=1);--
for(int i=1;i&=n;++i)tree[leaf+i]=i;
inline void modify(int x,int y) {
dis[x]=y,x+=leaf,x&&=1;
while(x) tree[x]=check(tree[x&&1],tree[x&&1|1]),x=x&&1;
void dijkstra(int s) {
for(int i=1;i&=n;++i) {
ans[u]=dis[u];
const int disu=dis[u];
modify(u,INF);
for(int j=head[u];j;j=edge[j].next){
int v=edge[j].v;
if(dis[v]&INF&&dis[v]&disu+edge[j].w)
modify(v,disu+edge[j].w);
u=tree[1];
inline void put(int x)
if (x & 9) put(x / 10);
putchar(x % 10 + 48);
int main() {
n=read(),m=read(),k=read();
for(int a,b,c,i=1;i&=m;++i) {
a=read(),b=read(),c=read();
add_edge(a,b,c);
dijkstra(k);
for(int i=1;i&=n;++i) {
if(dis[i]==0x3f3f3f3f)ans[i]=INF;
// put(ans[i]), putchar(' ');
printf("%d ",ans[i]);
乱写的SPFA,自认为挺好理解的适合新手使用 什么优化都没加(差点ML而且E要是有一个测试点把所有边都连到同一个点上就201了)
//好像并不需要但是习惯性的加这个(就不用写min函数和max函数了还可以乘方和求log);
var q:array[0..200001]//队列
t,f:array[0..10001]//t:以某顶点为起点的边数 f:最终起点到某点的最短路径值
vis:array[0..10001]//是否在队中
w,aim:array[0..01]//w:某边权值 aim:某边终点(p.s.第二个参数为了防坑开到1000本题150就能A)
n,m,s,x,y,z,i,j,head,tail:
readln(n,m,s);
for i:=1 to m do
readln(x,y,z);
inc(t[x]);//统计以x为起点的边数
aim[x,t[x]]:=y;//记录以x为起点的最新一条边终点为y,权值为z
w[x,t[x]]:=z;
//读入完毕
for i:=1 to n do f[i]:= f[s]:=0;//翻译:初始化所有顶点到起点的距离为题目要求的那个数(maxlongint)并且起点到起点的距离为0(不要忘记!)
q[1]:=s; head:=0; tail:=1; vis[s]:=// 翻译:队列第一个元素为起点;初始化队列; 标记起点已在队列中
while head&=tail do
inc(head);//翻译:队首出队
vis[q[head]]:=//翻译:标记队列头的元素已不在队列中
for i:=1 to t[q[head]] do//翻译:从1到以队列头为起点的总边数与枚举所有边
if f[aim[q[head],i]]&f[q[head]]+w[q[head],i] then//翻译:如果起点到队列头的元素的第i条边的终点的距离大于起点到队列头的元素的距离加上队列头的第i条边的权值
f[aim[q[head],i]]:=f[q[head]]+w[q[head],i];//更新起点到队列头的元素的第i条边的终点的距离为起点到队列头的元素的距离加上队列头的第i条边的权值
if not vis[aim[q[head],i]] then//如果不在队列中则入队
inc(tail); q[tail]:=aim[q[head],i];//入队操作
vis[aim[q[head],i]]:=//标记
for i:=1 to n do write(f[i],' ');//输出答案
本题解有以下好处:
代码注释详细
想背代码背不下来的同学可以背中文然后考试的时候实现就可以了
来一份题解,嗯。。真好吃。
新手刚学spfa(太菜了),第一次自己写过,在此发表题解留念,还望各位巨佬及大神指点批评。
好,言归正传。本题根据数据边数m&=500000,邻接矩阵存不下,只能使用静态邻接表存储。然后见楼下有写dijkstra+堆优化的,有floyd优化的,有各种牛X方法,本人太菜,只会套模板,这里提供一份教科书式的邻接表+spfa的解决方法共大家参考。
具体代码如下:
#include&bits/stdc++.h&
const long long inf=;
const int maxn=10005;
const int maxm=500005;
int n,m,s,num_edge=0;
int dis[maxn],vis[maxn],head[maxm];
struct Edge
int next,to,
}edge[maxm]; //结构体表示静态邻接表
void addedge(int from,int to,int dis) //邻接表建图
{ //以下是数据结构书上的标准代码,不懂翻书看解释
edge[++num_edge].next=head[from]; //链式存储下一条出边
edge[num_edge].to= //当前节点编号
edge[num_edge].dis= //本条边的距离
head[from]=num_ //记录下一次的出边情况
void spfa()
queue&int& //spfa用队列,这里用了STL的标准队列
for(int i=1; i&=n; i++)
dis[i]= //带权图初始化
vis[i]=0; //记录点i是否在队列中,同dijkstra算法中的visited数组
q.push(s); dis[s]=0; vis[s]=1; //第一个顶点入队,进行标记
while(!q.empty())
int u=q.front(); //取出队首
q.pop(); vis[u]=0; //出队标记
for(int i=head[u]; i=edge[i].next) //邻接表遍历,不多解释了(也可用vector代替)
int v=edge[i].
if(dis[v]&dis[u]+edge[i].dis) //如果有最短路就更改
dis[v]=dis[u]+edge[i].
if(vis[v]==0) //未入队则入队
vis[v]=1; //标记入队
q.push(v);
int main()
cin&&n&&m&&s;
for(int i=1; i&=m; i++)
int f,g,w;
cin&&f&&g&&w;
addedge(f,g,w); //建图,有向图连一次边就可以了
spfa(); //开始跑spfa
for(int i=1; i&=n; i++)
if(s==i) cout&&0&&" "; //如果是回到自己,直接输出0
else cout&&dis[i]&&" "; //否则打印最短距离
应该讲得非常清楚了,再不懂就翻书吧。(虽然我曾经也不懂)
一道适合在noip之前刷的模板题,有一些细节要注意:
1、本题为无向图(因为看成无向图而爆0的蒟蒻)
2、N&=10000,M&=500000说明只能用邻接表而非邻接矩阵
剩下的就只是标准的spfa了
#include&iostream&
#include&string&
#include&string.h&
#include&stdio.h&
#include&algorithm&
struct node{
int a,b,w;
}sq[1000500];
int visit[500500],dist[500500],n,m,s,
inde[500500],team[500500];//inde存的是每个点在图中有几条连向其他点的边,好像不能用index做数组名
bool cmp(node x,node y)
return ((x.a&y.a) || ((x.a==y.a) && (x.b&y.b)));
}//对sq排序,方便在spfa时循环
void spfa()
int i,j,f=0,r=1;
memset(visit,0,sizeof(visit));
for (i=1;i&=n;i++) dist[i]=;
dist[s]=0;team[r]=s;visit[s]=1;
while (f&r)
f++;i=team[f];
for (j=inde[i-1]+1;j&=inde[i];j++)
if (dist[i]+sq[j].w&dist[sq[j].b])
dist[sq[j].b]=dist[i]+sq[j].w;
if (!visit[sq[j].b])
{r++;visit[sq[j].b]=1;team[r]=sq[j].b;}
visit[i]=0;
void out()
for (i=1;i&=m;i++)
cout && sq[i].a && ' ' && sq[i].b && ' ' && sq[i].w &&
for (i=1;i&=n;i++)
cout && inde[i] && ' ';
int main()
scanf("%d%d%d",&n,&m,&s);
memset(inde,0,sizeof(inde));
for (i=1;i&=m;i++)
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
sq[i].a=x;sq[i].b=y;sq[i].w=z;
inde[x]++;
sort(sq+1,sq+1+m,cmp);
for (i=1;i&n;i++) inde[i+1]+=inde[i];//储存前缀和,方便循环
for (i=1;i&=n;i++)
if (dist[i]==) printf(" ");
else printf("%d ",dist[i]);
SPFA模板题,手打queue轻松过。
因为STL里的队列比较慢,所以手打。
大体思路是先使用一个队列,在开始的时候从起始点开始判断,如果与当前所连的边能更新权值,那就将其放入队列,不断地进行松弛操作。
SPFA最重要的有几点:
1.用邻接链表存图。
2.队列初始化。
3.队列更新操作。
4.如果边权太多怎么办?普通的SPFA会将时间复杂度提高到O(n^2).所以我们要用一个vis数组来判断元素是否在队列中。如果在,更新时就不必再放进去。这样会大大降低时间复杂度(虽然SPFA的时间复杂度一直都是玄学orz)
具体代码如下
#include&iostream&
#include&cstdio&
#include&cstring&
int n,m,s;
struct pan
int to,next,
}run[510000];//邻接链表存图,来节省空间
int head[500100],vis[510000],dis[510000],queue[510000];
int node=0;
void init()
memset(dis,0x3f,sizeof(dis));
for(int i=1;i&=m;i++)
head[i]=-1;
void add(int u,int v,int w)
run[++node].to=v;
run[node].val=w;
run[node].next=head[u];
void spfa()
int u,l=0;
queue[1]=s;
dis[s]=0;//上面几步为SPFA的初始化
while(l&r)
u=queue[++l];//队列的第一个元素出列,然后找该元素相连的其它点
vis[u]=0;//出列操作
for(int i=head[u];i!=-1;i=run[i].next)
if(dis[run[i].to]&dis[u]+run[i].val)//如果新的边比原先的短,那就更新权值
dis[run[i].to]=dis[u]+run[i].
if(vis[run[i].to]==0)//如果被更新的点原先不在队列里,就把它加进来,这样做的好处是避免了每一个点的重复询问,降低了时间复杂度
vis[run[i].to]=1;
queue[++r]=run[i].//被更新的点加入队列
int main()
int u,v,w;
cin&&n&&m&&s;
for(int i=1;i&=m;i++)
cin&&u&&v&&w;
add(u,v,w);
spfa();//SPFA主体
for(int i=1;i&=n;i++)
if(i==s) cout&&0&&" ";
if(dis[i]==0x3f3f3f3f) cout&&&&" ";//如果没有被连,那就输出
else cout&&dis[i]&&" ";
最短路的算法楼下已经介绍得很清楚了,这里介绍一种Dijkstra的奇异优化。
楼下似乎已经是有了堆优化了吧,而对于Dijkstra,既然要查询当前点中dis的最小值,那么我们何不采用线段树呢?
每取一个点,把它的值改成max_int表示这个点已经访问过,其他的点的值也可以直接在线段树上修改,每次查询整个线段树的最小值即可。
然后有一些小细节,比如说线段树存下的是点的编号,这样会好写一点吧。
大概复杂度也是mlogn吧,但常数大概会比priority_queue小一点吧,还跑的挺快的 192ms。
具体看代码吧。
#ifndef X_CPP
#define X_CPP
#include&cstdio&
#include&cstring&
#define Files "work"
#undef read
#undef write
#ifdef Files
#undef redir
#define redir(name) freopen(name".in","r",stdin),freopen(name".out","w",stdout)
extern inline char gc(){
static char buf[1&&17],*p1=buf,*p2=
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1&&17,stdin),p1==p2)?EOF:*p1++;
#define gc getchar
template &class T&
extern inline void read(T&n){
int sign=1;register char ch=gc();
for(n=0;(ch&'0'||ch&'9')&&ch!='-';ch=gc());
for(ch=='-'?ch=gc(),sign=-1:0;ch&='0'&&ch&='9';ch=gc()) n=(n&&1)+(n&&3)+ch-'0';
#ifdef Files
namespace out {
char buf[1&&17],*p1=buf,*p2=buf+(1&&17);
extern inline void pc(register char ch) {
*(p1++)=ch,p1==p2?fwrite(buf,1,p1-buf,stdout),p1=buf:0;
#define pc putchar
template &class T&
extern inline void write(T val) {
if(val&0) pc('-'),val=-
if(!val) pc('0');
register int num=0;
char ch[24];
while(val) ch[++num]=val%10+'0',val/=10;
while(num) pc(ch[num--]);
#ifndef _STL_ALGOBASE_H
#undef max
#undef min
template &class T& inline T max(const T a,const T b){return a&b?a:b;}
template &class T& inline T min(const T a,const T b){return a&b?a:b;}
template &class T& inline void ckmax(T&a,const T b){a&b?a=b:0;}
template &class T& inline void ckmin(T&a,const T b){a&b?a=b:0;}
const int N=10010,M=500010;
int pre[N],nx[M],to[M],w[M],
inline void add(int u,int v,int c){
nx[++cnt]=pre[u],pre[u]=cnt,to[cnt]=v,w[cnt]=c;
int tree[N&&2];
int dis[N],n,m,s;
inline void build(){
leaf=1; memset(dis,0x3f,sizeof(dis));
while(leaf&=n) leaf=leaf&&1;--
for(register int i=1;i&=n;++i) tree[i+leaf]=i;
inline int ck(unsigned short i,unsigned short j){
return dis[i]&dis[j]?i:j;
inline void change(int x,int y){
dis[x]=y,x+=leaf,x=x&&1;
while(x) tree[x]=ck(tree[x&&1],tree[x&&1|1]),x=x&&1;
int ans[N];
inline void dj(){
register unsigned short u=s,tot=0;;
while(tot&=n){
++tot,ans[u]=dis[u];
const int disu=dis[u];change(u,0x7fffffff);
for(register int e=pre[u];e;e=nx[e])
if(dis[to[e]]&0x7fffffff&&dis[to[e]]&disu+w[e]) change(to[e],disu+w[e]);
u=tree[1];
int main(){
#ifdef Files
if(fopen(Files".in","r")) redir(Files);
read(n),read(m),read(s);
for(register int i=1,u,v,c;i&=m;++i) read(u),read(v),read(c),add(u,v,c);
for(register int i=1;i&=n;++i) write(ans[i]==0x3f3f3f3f?:ans[i]),pc(' ');
#ifdef Files
fwrite(buf,1,p1-buf,stdout);
我来写一个C++版指针邻接表的题解.这个题目已经有不少题解,写这个题解的原因是大家大多数介绍的是前向星之类的算法,没有指针邻接表,然而相信写指针邻接表的同学也不少.
思路:Bellman-Ford算法,外加SPFA
先跟大家说一说为什么选BF算法吧.最短路主要有三种算法,Floyd,Dijkstra和Bellman-Ford.
首先看时间复杂度 Floyd是O(N^3) (指N的三次方,下同,不是位运算),Dijkstra是O(MlogN+NlogN),Bellman-Ford是O(NM)
于是Floyd就首先被排除掉了.
空间复杂度 Floyd O(N^2) 剩下两种都是O(M) 没有问题
适用情况,dijkstra和Floyd都比较适合稠密图,因为它们都和顶点密切相关,而Bellman-Ford较适用稀疏图,因为跟边密切相关.
而题中m远小于n^2,所以用Bellman-Ford
代码解释见注释
#include &iostream&
const int maxn=10005,maxm=500005;
//大家务必注意数据范围
const int inf=;
//因为题目如果不联通就输出,所以inf设成这个数
struct Node
}*h[maxn],pool[maxm];
//标准指针邻接表
int n,m,i,j,k;
int dis[maxn],vis[maxn];
//dis[x]指起始点离x的最短距离,vis表示是否在队列里
int q[maxm],head=1,tail=1;
//spfa的队列
int tot=0;
void addEdge(int u, int v, int c)
Node *p=&pool[++tot];
p-&next=h[u];
int main()
cin&&n&&m&&
for(int i=1;i&=n;i++) dis[i]=
dis[root]=0;
for(int i=1;i&=m;i++)
int x,y,z;
cin&&x&&y&&z;
addEdge(x,y,z);
vis[root]=1;
//起始点入队
while(head&tail)
k=q[head];
for(Node *p=h[k];p;p=p-&next)
//遍历k连着的所有点.第二个分号里的p实际上是p!=NULL的简写
if(dis[p-&v]&dis[k]+p-&c)
//Bellman-Ford核心语句,松弛
dis[p-&v]=dis[k]+p-&c;
if(vis[p-&v]==0)
q[tail]=p-&v;
vis[p-&v]=1;
vis[q[head]]=0;
for(int i=1;i&=n;i++)
cout&&dis[i]&&' ';
这个困惑我许久的链表终于有一点改善了,这道题显然不能用Floyd,会炸
那么我们怎么办呢?(把大家当傻瓜的说),那么请见下图:
简单来说就是~~:
a.初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其他顶点,即:U={其余顶点},若v与U中顶点u有边,则&u,v&正常有权值,若u不是v的出边邻接点,则&u,v&权值为∞。
b.从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。
c.以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。
d.重复步骤b和c直到所有顶点都包含在S中。
实在没有看懂的话可以去看一个很赞的博客: 最短路径—Dijkstra算法和Floyd算法 - as_ - 博客园(请叫我雷锋)
代码奉上:
#include&iostream&
#include&cstdio&
#include&algorithm&
#include&climits&
#define MAX
#define maxn 50001
struct node
int to,next=0,w;
}edge[maxn];
int head[maxn];
int dis[maxn],n,m,k,t;
//struct存边 ,dis存距离,vis判断遍历条件;
void dj(int k)
bool vis[maxn]={false};
for(int i=1;i&=n;i++)
int mi=MAX,t=-1;
//定义判断条件;
for(int j=1;j&=n;j++)
//遍历每个点;
if(!vis[j] && dis[j]&mi)
//未遍历过且连接有边 ;
mi=dis[j];
//存边,存最小值 ;
if(t==-1 || mi==MAX)
//MAX就是无限大,也就理解为没有边;
//即第j个点已经遍历过了;
//接下来是本羸弱最不能理解的地方(链表没学好 ~ —o —~ );
for(int j=head[t];j;j=edge[j].next)
//从head头找起,一个一个连上找;
//如果该点没有找过,并且源点到该点的距离更小;
if(!vis[edge[j].to] && dis[edge[j].to]&(edge[j].w+dis[t]))
dis[edge[j].to]=edge[j].w+dis[t];
//那么更新;
int main()
cin&&n&&m&&k;
for(int i=1;i&=m;i++)
//初始化所有的点都不连接;
dis[i]=MAX;
//到开始的点的距离为0 ;
for(int i=1;i&=m;i++)
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
//简化输入,应该都懂吧(如果不懂请见《啊哈,算法》);
edge[i].to=y;
edge[i].w=z;
edge[i].next=head[x];
head[x]=i;
dj(n);//开始查询!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
for(int i=1;i&=n;i++)
printf("%d ",dis[i]);
//完美输出
如果还不懂,那你很棒棒;
我无能为力了。
看看别的大佬写的代码吧
更好的体验:
千年不过的不用指针的spfa;
原来各位dalao是骗我的!
根本不用ff数组来判重!
加上这题目pas题解比较少就来谈谈5种AC的思路(顺便总结一下);
①floyd算法 时间:O(n^3);空间:O(n^2);
对于20%的数据:N&=5,M&=15
对于40%的数据:N&=100,M&=10000
②dijkstra算法
时间: O(n^2); 空间:O(n^2);
对于70%的数据:N&=1000,M&=100000
const maxn=5000;
var g:array[1..maxn,1..maxn]
d:array[1..maxn] //pre[i]指最短路径上I的前驱结点
n,m,s,x,y,w,i,j:
procedure dijkstra(v0:longint);
var u:array[1..maxn]
i,j,k: min:
fillchar(u,sizeof(u),false);
for i:=1 to n do d[i]:=g[v0,i];
for i:=1 to n-1 do begin
//每循环一次加入一个离1集合最近的结点并调整其他结点的参数
min:=; k:=0; //k记录离1集合最近的结点
for j:=1 to n do
if (not u[j]) and (d[j]&min) then begin
k:=j; min:=d[j];
for j:=1 to n do
if (not u[j]) and (g[k,j]+d[k]&d[j]) then d[j]:=g[k,j]+d[k];
readln(n,m,s);
for i:=1 to n do
for j:=1 to n do if i=j then g[i,j]:=0 else g[i,j]:=;
for i:=1 to m do begin
readln(x,y,w);
g[x,y]:=min(w,g[x,y]);
dijkstra(s);
//找出最短路径
for i:=1 to n do write(d[i],' ');
③dijkstra+前向星空间优化+堆优化
时间: O(n log n); 空间:O(kn);
对于100%的数据:N&=10000,M&=500000
type rec=record
rec2=record
const inf=;
maxm=500000;
maxn=10000;
var i,j,n,m,s,x,y,z,tot,nd:
d,head:array[-maxn..maxn]
a:array[-maxm..maxm]
dui:array[0..4*maxm]of rec2;
procedure swap(var a,b:rec2);
var t:rec2;
t:=a; a:=b; b:=t;
procedure adde(u,v,w:longint);
inc(tot); a[tot].en:=v;
a[tot].pre:=head[u];
a[tot].w:=w;
procedure swap(var a,b:longint);
begin t:=a;a:=b;b:=t;
procedure up(x:longint);//将一个结点“上浮”
while x&1 do begin //没有上浮到最顶层
if dui[x].val&dui[x div 2].//如果上方的结点小于此节点,则暂停上浮
swap(dui[x],dui[x div 2]);//交换上方结点与此结点
x:=x div 2;
procedure down(x:longint);//将一个节点“下沉”
while x&nd do begin
y:=x+x;//y是x的左儿子
if y&//x已经沉到底部
if (y&nd)and(dui[y+1].val&dui[y].val) then inc(y);//如果x存在右儿子,且右儿子比左儿子小,则将y赋值到右儿子
if dui[x].val&=dui[y].//若两个儿子中的较小值仍然比x大,则停止下沉
swap(dui[x],dui[y]);//下沉
function pop():
pop:=dui[1].
swap(dui[1],dui[nd]);//将最后的结点(保证其没有儿子)与最顶端交换
down(1);//下沉顶端
procedure dijkstra(v0:longint);
var i,j,k,minn,u,v,p:
vis:array[-maxn..maxn]
fillchar(vis,sizeof(vis),false);
for i:=1 to n do d[i]:=
dui[1].val:=0;
dui[1].id:=v0;
for i:=1 to n do begin
while vis[u] and (nd&0) do u:=pop();
p:=head[u];
while p&0 do begin
if (not vis[v]) and(d[u]+a[p].w&d[v]) then begin
d[v]:=d[u]+a[p].w;
dui[nd].id:=v;
dui[nd].val:=d[v];
readln(n,m,s);
for i:=1 to m do begin
readln(x,y,z);
adde(x,y,z);
dijkstra(s);
for i:=1 to n do
if d[i]=inf then write(,' ')else write(d[i],' ');
④ spfa队列优化+指针邻接表空间优化
时间: O(KE); 空间:O(KE);
对于100%的数据:N&=10000,M&=500000;
node=record
const maxn=50040;
var dis:array[0..maxn]
a:array[0..maxn]
n,m,b,u,v,x,y,i,j,l,w,s:
procedure adde(u,v,w:longint);
p^.pre:=a[u]^.
p^.en:=v; p^.w:=w;
a[u]^.pre:=p;
var f:array[0..maxn]
q:array[0..4*maxn]
head,tail,u,v:
q[1]:=s; dis[s]:=0;
head:=0; tail:=1;
fillchar(f,sizeof(f),false);
while head&tail do begin
inc(head); u:=q[head]; p:=a[u];
while p^.pre&&nil do begin
p:=p^. v:=p^. w:=p^.w;
if dis[v]&dis[u]+w then begin
dis[v]:=dis[u]+w;
if not f[v] then begin
inc(tail); q[tail]:=v;
readln(n,m,s);
for i:=1 to n do begin
new(a[i]);
a[i]^.pre:=
for i:=1 to m do begin
readln(u,v,w);
adde(u,v,w);
for i:=1 to n do
dis[i]:=maxlongint div 3;
for i:=1 to n do
if dis[i]=maxlongint div 3 then write(' ')
else write(dis[i],' ');
close(input);
close(output);
⑤ spfa队列优化+链式前向星空间优化
时间: O(KE); 空间:O(KE);
对于100%的数据:N&=10000,M&=500000
type rec=record
var n,m,s,x,y,z,tot,i:
head,q,d:array[1..1000000]
a:array[1..1000000]
procedure adde(u,v,w:longint);
a[tot].en:=v;
a[tot].pre:=head[u];
a[tot].w:=w;
var h,t,i,v,u:
for i:=1 to n do d[i]:=
q[1]:=s; d[s]:=0;
h:=0; t:=1;
while h&t do begin
i:=head[u];
while i&0 do begin
if d[v]=maxlongint then begin
d[v]:=d[u]+a[i].w;
inc(t);q[t]:=v;
if d[v]-d[u]&a[i].w then begin
inc(t);q[t]:=v;
d[v]:=a[i].w+d[u];
readln(n,m,s);
for i:=1 to m do begin
readln(x,y,z);
adde(x,y,z);
for i:=1 to n do
if d[i]=maxlongint then write(' ')
else write(d[i],' ');
单源最短路径最快的用贝尔曼福特算法
稍微优化了一下
大概思路就是
先把所有点到一个点的距离定为无穷大
如果有直接到的就附初值
然后一个一个点看能否通过这个点使其他点到终点的路径减短
附上代码:
#include&cstdio&
#include&algorithm&
struct note
int cmp(const note &a,const note &b)
if(a.start&b.start)
if(a.start&b.start)
if(a.end&b.end)
struct note que[500005];
int main()
int i,k,n,m,s;
int con[10005]={0},dis[10005];
int dui[50010],book[10005];
int head=1,tail=2;
scanf("%d %d %d",&n,&m,&s);
for(i=1;i&=n;i++)
dis[i]=9999999;
for(i=1;i&=m;i++)
scanf("%d %d %d",&que[i].start,&que[i].end,&que[i].length);
sort(que+1,que+1+m,cmp);
for(i=1;i&=m;i++)
if(que[i].start!=que[i-1].start)
con[que[i].start]=i;
book[s]=1;
while(tail&head)
if(con[dui[head]]==0)
i=con[dui[head]];
while(que[i].start==dui[head])
if(dis[que[i].end]&dis[que[i].start]+que[i].length)
dis[que[i].end]=dis[que[i].start]+que[i].
if(book[que[i].end]==0)
dui[tail]=que[i].
book[dui[tail]]=1;
book[dui[head]]=0;
for(i=1;i&=n;i++)
if(dis[i]==9999999)
printf(" ");
printf("%d ",dis[i]);
dalao们都说朴素Dijkstra不行,很多还说Dijkstra即使加优化也不行……
本蒟蒻因为不会写SPFA 只好写Dijkstra 然后提交无数次失败 无数次调试后 堆优迪杰和朴素迪杰都过了。
因为已经有用堆优迪杰的题解了,这里只贴上自己的代码用于对比,(很好奇为什么我的朴素不会TLE 233)
#include&cstdio&
#include&iostream&
#define MAXN 10021
#define INF 0x7fffffff
struct Edge {
int succ, value,
int n, m, s, t,
int h[MAXN], vis[MAXN], dist[MAXN];
Edge graph[1000021];
int main() {
scanf("%d%d%d", &n, &m, &s);//读入,n为点数,m为边数,s为起点
flag =//用于记录扩展次数的变量
for (int i=1; i&=m; i++) {
scanf("%d%d%d", &a, &b, &c);
graph[++t] = {b, c, h[a]};
fill(dist+1, dist+n+1, INF);//赋各点到起点的距离为无穷大
dist[s] = 0;
while (flag--) {
int mini = INF, num = 0;
for (int i=1; i&=n; i++)
if (!vis[i]) {
num = dist[i] & mini ? i :
mini = min(mini, dist[i]);
}//找到还没被访问的与s距离最小的点num
vis[num] = 1;//将num扩展进集合
for (int i=h[num]; i=graph[i].next)
dist[graph[i].succ] = min(dist[graph[i].succ], dist[num] + graph[i].value);
}//对num的所有邻居更新dist
for (int i=1; i&=n; i++)
printf("%d ", dist[i]);//输出
puts("");//个人习惯,输出一个换行符,不写一样
还有一个值得讨论的地方 我的赋初值放在while内之后AC,但是放在while内会WA三个点,好奇。
※注:flag变量的使用仅仅是为了少一个循环变量,用for效果一样
手打裸spfa,坚持发题解(看别人风格不同的代码会很吃力,都有体会吧?)
如果你只得了一部分分数,考虑一下一下最容易出错的两种情况:
①那串长的数没输出或者有问题②memset就是个坑,dis初始化用for循环(卡了n遍90,最后求助本校神犇)
#include&iostream&
#include&cstdio&
#include&cstring&
#define rq
#define maxn 500010
struct EDGE
}edge[maxn*5];
int qr,n,m,s,minn=0x7f,head[maxn],dis[maxn],exist[maxn];
int team[maxn*5],u;
void add(int from,int to,int co)
edge[++qr].next=head[from];
edge[qr].to=
edge[qr].co=
head[from]=
void spfa()
for(int i=1;i&=n;++i)
memset(exist,0,sizeof(exist));
int h=0,t=1;
dis[s]=0;exist[s]=1;team[1]=s;
while(h&t)
u=team[h];
exist[u]=0;
for(int i=head[u];i!=0;i=edge[i].next)
int v=edge[i].
if(dis[v]&dis[u]+edge[i].co)
dis[v]=dis[u]+edge[i].
if(!exist[v])
exist[v]=1;
team[t]=v;
int main()
ios::sync_with_stdio(false);
cin&&n&&m&&s;
for(int i=1;i&=m;++i)
int a,b,c;
cin&&a&&b&&c;
add(a,b,c);
for(int i=1;i&=n;++i)
if(dis[i]==66666) printf("%d ",rq);
else printf("%d ",dis[i]);
什么是前向星
前向星是一种效率高,使用简便的存图方式,和其他大部分方式比,有着很大的优势:
● 对于动态数组,不用使用麻烦的STL和队列
● 对于邻接链表,不用操作指针
● 对于邻接矩阵,效率很高
一个数据结构,里面的成员可以存储起点,终点和权值,要有一个数组维护每点连出去的边的起点。
链式前向星
上面的前向星是需要排序的,事实上大部分时间我们使用的都是链式前向星。链式前向星就是数组模拟链表,其基本实现如下:
struct edge
//设i为这条边的起点,它指向以这条边为起点的下一条边
//这条边的终点
还有一个head数组用来维护以i为起点的第一条边,里面初始化为全负一。
前向星,顾名思义,我们应该让后面的边指向前一条边,这可能有点不直观。事实上,前向星存边是倒着存的。当加入新的边,这条边就是第一条边,同时链接到之前的第一条边,因此遍历时实际上是倒着遍历的。
void add(int w)
edge[cut].w=w;
edge[cut].to=
edge[cut].next=head[from];
head[from]=
从第一条边开始,e[k].[next]中即为下一条边的编号,从而遍历以sp为起点的所有边,当边的编号为-1时即跳出循环。
for(k=head[sp];k!=-1;k=e[k].next)//源点为sp
#include&iostream&
#include&cstring&
#define MAXN 10005
#define MAXM 500005
#define MAXI
struct edge{
//从哪条边出发
//到哪条边
//上一条同起点的边
void add(int u,int v,int w);
void spfa();
edge e[MAXM];
int head[MAXM];
int n,m,s,fi,gi,wi,i,cut=0;
int map[MAXN][MAXN];
int dis[MAXN];
int v[MAXN];
int push[500005];
int main(){
cin&&n&&m&&s;
for(i=1;i&=m;i++)
head[i]=-1;
for(i=1;i&=m;i++){
cin&&fi&&wi&&
add(fi,wi,gi);
for(i=1;i&=n;i++){
dis[i]=MAXI;
for(i=1;i&=n;i++){
cout&&dis[i]&&' ';
void spfa(){
int k,j,h,t,
push[1]=s;
while(h&t){
sp=push[h];
for(k=head[sp];k!=-1;k=e[k].next){//前向星遍历,e[k].to等价于邻接矩阵的map[sp][k]
if(dis[e[k].to]&e[k].w+dis[sp]){
dis[e[k].to]=e[k].w+dis[sp];
if(v[e[k].to]==0){
v[e[k].to]=1;
push[t]=e[k].
void add(int u,int v,int w){
e[cut].w=w;
e[cut].from=u;
e[cut].to=v;
e[cut].next=head[u];//上一条边是第一条边
head[u]=//把这条边变成第一条边
弱鸡花了好久。。
这个是个dijkstra的优先队列优化,用的vector 存储边
之前数组开10000就SE了 开大果然好了。。
还有STL大法好 233
#include&iostream&
#include&cstdio&
#include&algorithm&
#include&utility&
#include&vector&
#include&queue&
#define MAXN 20000
#define INF
typedef pair&int,int&
priority_queue&pii,vector&pii&,greater&pii& &
struct edge
vector&edge& G[MAXN];//g[i]--i to g[i].to cost cost
int n,m,s;
int dis[MAXN];
void dijk(int s)
for(int i=1;i&=n;i++)
dis[i]=INF;
pq.push(make_pair(0,s));
// cout&&dis[s]&&
while(!pq.empty())
pii u = pq.top();
int x = u. // bian hao
//cout&&x&&
for(int i=0;i&G[x].size();i++)
edge e=G[x][i];
if(dis[e.to]&dis[x]+e.cost)
dis[e.to]=dis[x]+e.
pq.push(make_pair(dis[e.to],e.to));
// cout&&dis[e.to]&&
int main()
cin&&n&&m&&s;
int from,to,
for(int i=0;i&m;i++)
scanf("%d%d%d",&from,&to,&cost);
in.to= in.cost=
G[from].push_back(in);
for(int i=1;i&=n;i++)
printf("%d ",dis[i]);
看到各位大佬都上了各种各样的SPFA,本蒟蒻只好弱弱地上一个dijstra(不用堆优化)。
dijstra的思路很简单,基本上就可以算是一个DP,就是找到与起点距离最短距离一定的点,然后继续往外拓展,更新其它点对于起点的最短距离。注意:在所有已知距离起点的距离中,最短的那个就是新的起点(因为它距离起点的距离不能再短了)。将整张图搜完,就大功告成了。
本人还加了一个前向星存边优化(开始直接暴搜,二维存边,只得了10分......)(前向星是什么东西?看本人以前的题解或网上去查一下就行了),这样就可以进行优化,直达AC!
下为代码:
#include&cstdio&
#include&iostream&
#include&cstring&
#include&cmath&
#include&algorithm&
#include&string&
struct Edge//前向星存边
//此边的子节点
//此边的权值
//与它最近的父节点一样的边的编号
}edge[1000000];
int head[20000];//以某点为父节点引出的最后一条边
int cnt=0;//边编号
inline void add(int a,int b,int c)//存边
edge[cnt].z=b;
edge[cnt].val=c;
edge[cnt].nexty=head[a];
head[a]=//更新head
int main()
bool visit[20000]={0};//是否作为过起点
long long dis[20000];//距离
int n,m,s;
int a,b,c;
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i&=n;i++)dis[i]=;
for(int i=0;i&m;i++)
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
int curr=s;
while(!visit[curr])//即搜完整张图
visit[curr]=//已做为过起点
for(int i=head[curr];i!=0;i=edge[i].nexty)//链式前向星搜边
if(!visit[edge[i].z]&&dis[edge[i].z]&dis[curr]+edge[i].val)
dis[edge[i].z]=dis[curr]+edge[i].//更新操作
for(int i=1;i&=n;i++)
if(!visit[i]&&minn&dis[i])//取新的最小值
minn=dis[i];
for(int i=1;i&=n;i++)printf("%lld ",dis[i]);
这里是一个相对较快的SPFA模板(不到400ms)
总而言之,SPFA的关键还是队列优化,这点写顺了BFS的同学可能会觉得比较相似
邻接表还是比较好用的,当初理解也花了挺久。。
#include&bits/stdc++.h&
#define rep(i,j,n) for(int i=j;i&=n;i++)
const int N=10010,M=500010;
inline int read(){
//读入优化
while(ch=getchar(),ch&'9'||ch&'0') ; x=ch-'0';
while(ch=getchar(),ch&='0'&&ch&='9') x=(x&&1)+(x&&3)+ch-'0';
int head[N],dis[N],n,m,s,x,y,w,
//head[x]为点x连接的第一条边,dis[i]为所求距离,cnt为边总数
struct edge{
//邻接表插边
int next,to,w;
//next为边e[i]的下一条边 , to为边e[i]的终点
inline void ins(int x, int y, int v) {
cnt++; e[cnt]=edge{head[x],y,v} ; head[x]=
queue&int&
bool inq[N];
//是否在队中
int main(){
n=read() ; m=read() ; s=read();
rep(i,1,m) {
x=read() ; y=read() ; w=read();
ins(x,y,w);}
rep(i,1,n) dis[i]=;
//别的题这里可能会有问题,最好结合实际选择
q.push(s); dis[s]=0; inq[s]=
//源点入队
while(!q.empty()) {
int x=q.front() ; q.pop(); inq[x]=
for(int i=head[x]; i=e[i].next){
//遍历队首连出的所有边
int v=e[i].
if( dis[v]&dis[x]+e[i].w) {
dis[v]=dis[x]+e[i].w;
if(!inq[v]) inq[v]=true,q.push(v); //新点入队
rep(i,1,n) printf("%d ",dis[i]) ;
作为一道单源最短路径的题(模板),应该是需要用到Dijstra或者Bellman—Ford,本蒟蒻用的是Bellman—Ford(没有用队列来优化,但是还是加了优化),用Bellman—Ford的时间复杂度是O(NM),加上优化,也应该不会TLE
#include&bits/stdc++.h&
int u[500001],v[500001],w[500001];//u存储第i条信息中的出发点,同理-----
long long dis[500001];//点 s 到第i点的距离
int n,m,s;
int main()
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);//这里加了加速cin,cout防止TLE
cin&&n&&m&&s;
for(int i=1;i&=n;i++)
dis[i]=;把每条边先附成这个值,方便输出
dis[s]=0;//s到s显然是0
for(int i=1;i&=m;i++)
cin&&u[i]&&v[i]&&w[i];//输入第i条信息
for(int i=1;i&=n-1;i++)//标准Bellman_ford
int ch=0;//判断在松弛中是否还会更新
for(int j=1;j&=m;j++)
if(dis[v[j]]&dis[u[j]]+w[j])
ch=1;//更新了
dis[v[j]]=dis[u[j]]+w[j];因为v[j]是一个点的编号,而n&=10000所以数组不会爆
if(ch==0)//再也更新不了了
for(int i=1;i&=n;i++) cout&&dis[i]&&" ";
SPFA+SLF优化
本题是个模板题,具体说明就放在代码中了QWQ
#include&iostream&
#include&cstdio&
#include&cstring&
#include&algorithm&
#include&cmath&
#include&deque&
struct arr{
}bot[1000000];
int head[11000];
long long dis[11000];
int f[11000];
int n,m,s,
deque&int&q;//定义一个双端队列
inline void add(int u,int v,int w){bot[++cnt].nd=v;bot[cnt].co=w;bot[cnt].nx=head[u];head[u]=}
//这里是用邻接链表存储边
inline void SPFA(int s){
for(int i=1;i&=n;i++) dis[i]=;
dis[s]=0;f[s]=1;
q.push_back(s);
while(!q.empty()){
int now=q.front();
q.pop_front();
for(int i=head[now];i;i=bot[i].nx){
int v=bot[i].
if(dis[v]&dis[now]+bot[i].co){
dis[v]=dis[now]+bot[i].
if(!f[v]){
if(q.empty()||dis[v]&dis[q.front()]) q.push_back(v);
else q.push_front(v);//SLF优化,不懂的可以去这里[SPFA的优化](http://tzdyy.lofter.com/post/1e3cd119_10c05810)
inline int read(){//读入优化
int x=0,w=1;char ch=0;
while(ch!='-'&&(ch&'0'||ch&'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch&='0'&&ch&='9')x=x*10+ch-48,ch=getchar();
return x*w;
int main(){
n=read();m=read();s=read();
int x,y,w;
for(int i=1;i&=m;i++) x=read(),y=read(),w=read(),add(x,y,w);
for(int i=1;i&=n;i++)
if(dis[i]!=) printf("%d ",dis[i]);
else printf(" ");//输出
一道很玄学的题,数据大到居然cin和cout都会超时,于是乎只好用了scanf和printf,真的是很坑啊。
回到正题,一道很水的最短路。
看到数据那么大,很自然的就用到了SPFA.
一个裸地SPFA的模板。
其实这是我的第二次提交了,第一次居然没过,很惊讶。
首先开一个队列,起点入队列,然后不断找与他相邻的边,再入队列,当发现通过这个点的进入时,有其他的路线变短了,如果没有在队列里,那么再次入队列,如果入过了就更新它的值。
以上就是SPFA的原理。
具体解释看代码。
PS:其实暴力DFS也是可以骗分的。
当然地泽斯特拉和贝尔曼福德也是可以的,但是可能会超时‘
#include&iostream&
#include&cstdio&
#include&cstdlib&
#include&cstring&
#include&cmath&
#include&algorithm&//头文件
#define maxn 100100
#define maxm 5000100
#define inf //无脑define
int n,m,p,i,s,t;
int h[maxm],v[maxm],w[maxm],next[maxm];
int q[maxn*5],d[maxm];
bool f[maxn];
void spfa(int start)//代码开始
int head=0,tail=1;
f[start]=//入队列
while(head&tail)//操作队列
i=q[head];//出队列
f[i]=//注意一定要更改值,因为可能会重复入队列
p=h[i];//下一条边
while(p&0)
if(d[i]+w[p]&d[v[p]])//松弛操作
d[v[p]]=d[i]+w[p];
if(f[v[p]]==false)//如果没用过就再次入队列
q[tail]=v[p];
p=next[p];//下一条边
void add(int i,int j,int k)//邻接表存图
next[p]=h[i];
int main()
int x,y,z;
cin&&n&&m&&s;//输入
for(int i=1;i&=n;i++)//也可以用memset
d[s]=0;//起点附0
for(int i=1;i&=m;i++)
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
for(int i=1;i&=n;i++)
printf("%d ",d[i]);//输出
return 0;//结束
很多dalao用的SPFA。。这儿贴上用朴素的queue来实现SPFA;
stl中的deque,priority等等也能实现,对于自己写队列来说,还要防止队列的空间假死。。
用queue来做,一般不会超时。。。。然而这道题目没有卡SPFA的常数。。。用dji来做的话,朴素的只能拿到40-60吧。
当然也可以单调队列优化。。。。
考虑到这是一个###模板题,直接queue贪心了。。。(貌似也不是贪心。。。)
另外这个不能用邻接矩阵。。。。不然。。。。。空间BOOM。。
用vector或者list都可以,,,,貌似list的push速度比vector在某些范围内跟快。。。。。
那就用queue吧。。。。初始化的时候 dis全部是,而是dis[s]=0;这个在输出的时候有用。。
// P3371### 【模板】单源最短路径.cpp :
#include&vector&
#include&iostream&
#include&algorithm&
#include&queue&
#include&cmath&
#include&list&
const int maxn = ;
struct nodes {
int u, to,
};//貌似edge来命名比较好,,这个是每条边的结构体。。。其实不需要u。。。(to表示末点,w表示长度)
int dis[10000 + 20];
typed//方便后面的定义。。
list&node& v[10000+20];//边集,list和vector都可以。。
int inq[10000 + 20]; //用inq来表示点是否在queue中,判重和记忆。。
queue&int&//不解释了、、
int main()
cin && n && m &&
for (int i = 0; i & i++) {
cin && x && y &&
v[x].push_back({ x,y,z });
//用list来读入边集
for (int i = 0; i & 10000 + 20; i++) dis[i] = ; //初始化的时候
dis全部是,而是dis[s]=0;这个在输出的时候有用。。
dis[s] = 0;
q.push(s);
inq[s] = 1;
//用来判断点是否在队列里面,,类似记忆。。
while (!q.empty()) {
int cur = q.front(); q.pop();//取出待松弛的边
inq[cur] = 0;
for (list&node&::iterator it = v[cur].begin(); it!=v[cur].end(); it++) {//遍历
if (dis[cur] + it-&w & dis[it-&to]) {//这个是松弛操作的核心。。如果过一点的距离+该点长度小于末点的dis值,就更新末点的dis
dis[it-&to] = dis[cur] + it-&w;
if (!inq[it-&to]) {//判断是否in queue
q.push(it-&to);//松弛之后,加入queue,等待接下来的松弛。。
for (int i = 1; i &= i++) {
cout && dis[i] && " ";
system("pause");
其中,cin没有读入优化过,想要AC不超时,,把cin改成scanf就可以了。。。。
理解思想,,才是重点!!!
C++STL是一个神一般的存在,在SPFA的实现上可以体现这一点:
#include&cstdio&
#include&string&
#include&vector&
#include&queue&
struct node
const int maxint=;
vector&node& g[10001];
int n,m,s,dist[10001];
bool f[10001];
void init()
scanf("%d%d%d",&n,&m,&s);
int i,a,b,w;
for(i=1;i&=n;i++)
if(i==s)dist[i]=0;
else dist[i]=
for(i=0;i&m;i++)
scanf("%d%d%d",&a,&b,&w);
p.e=w;p.id=b;
g[a].push_back(p);//vector实现的邻接表,代码相对简短
bool relax(int u,int v)
if(dist[g[u][v].id]&dist[u]+g[u][v].e)
dist[g[u][v].id]=dist[u]+g[u][v].e;
void SPFA()
queue&int&q.push(0);//队列,这个的应用更普遍
q.push(s);f[s]=
while(!q.empty())
q.pop();if(q.empty())
v=q.front();
for(i=0;i&g[v].size();i++)
if((relax(v,i))&&(f[g[v][i].id]))
q.push(g[v][i].id);
f[g[v][i].id]=
for(i=1;i&=n;i++)printf("%d ",dist[i]);
int main()
两个STL应用使代码简短了很多。然而还是那句话,天上不会掉馅饼,程序的效率还是有所下降的。然而,效率不是全部,人们宁可牺牲三倍效率用Java而不用C语言就是最好的例子(from_Charles E Leiserson_),具体取舍要看情况。
Dijkstra+优先队列
存图用的是数组模拟邻接表
由于优先队列不做其它定义的话 top() 返回是队列里最大的数
我们希望越小的数优先级越高,这里定义一个运算符:
bool operator & (const node&pd) const{
return d&pd.d;
扣上精(rong)美(chang)的代码:
#include&cstdio&
#include&algorithm&
#include&queue&
#include&cstring&
#define For(x) for (int i=1;i&=x;i++)
#define bl(x) for (int h=head[x],o=v[h];h;o=v[h=to[h]])//遍历操作
const long long N=;
const long long INF=~0u&&1;//将32位的0取反后右移一位,即
long long head[N],to[N],v[N],w[N],num,dis[N],vis[N],n,m,s,
struct node
long long d,
bool operator & (const node&pd) const{
return d&pd.d;//小的优先级高
void get_node(){
long long U,V,W;
scanf("%lld%lld%lld",&U,&V,&W);
to[++num]=head[U],head[U]=num,v[num]=V,w[num]=W;
priority_queue &node&
int main(){
scanf("%lld%lld%lld",&n,&m,&s);
for(long long i=0;i&m;i++) dis[i]=INF;//初始化
For(m) get_node();dis[s]=0;
q.push((node){0,s});
while (!q.empty()){
tmp=q.top();q.pop();
long long uu=tmp.
if (vis[uu])
bl(tmp.pos){
if (dis[o]&dis[uu]+w[h]){
dis[o]=dis[uu]+w[h];
q.push((node){dis[o],o});
vis[uu]=1;
For(n) printf("%lld ",dis[i]);
又暴力,又赤裸的***算法
(我已经不知道自己在干什么了)
看看大神们的教程,什么优化,我完全不会啊,所以就先写了个纯粹的单源最短路算法,可能是SPFA(我自创的你信吗)。啪一交过了,开心。
STL用的较多,那些喜欢纯粹的代码的同学对不住了。
我用一个类似链表的东西存储图。首先弄个点集(按编号有序),每个点存储着一个边的序列,对于边我记载了他的目标点和长度。
可以用数组实现点集(毕竟下标的方便),用 vector 实现边的序列(防炸,如果开10005的数组的话BOOM!),用 pair 记录边。
那就变成了这样:
typedef pair&int, int&
vector&mp& graph[10005];
这样做会避免邻接表和邻接矩阵带来的复杂度增加,而且方便。
接下来广搜这张图!
先把 dis 数组(记录到每个点的距离)全部初始化为 INF。
松弛就是:如果我现在从这条边走到你那里比你现在在 dis 数组里的值要短,那么就把你 dis 的值改掉。
从点 S 开始使用所有 S 的边松弛点 S 直接相连的点。这一步做完后,按同样的方法处理由点 S 松弛过的点,直到没有点可以处理。
是不是很简单?!接下来扒出超级美的代码:
#include &cstdio&
#include &queue&
#include &vector&
#include &utility& // for std::pair
const int INF();
typedef pair&int, int&
vector&mp& graph[10005];
int dis[10005], N, M ,S;
int main()
scanf("%d%d%d", &N, &M, &S);
for (int i(0); i & M; ++i)
int st, en,
scanf("%d%d%d", &st, &en, &len);
graph[st].push_back(mp(en, len)); // mp(int, int) 是一个构造函数,他构造了一个临时容器,可以把它推进vector
for (int i(1); i &= N; ++i) dis[i] = INF;
dis[S] = 0; // 自己到自己当然是0
queue&int& // 队列,广搜有用,应该见怪不怪了吧
nbs.push(S);
while (!nbs.empty()) // 简直广搜
int now(nbs.front());
for (mp x : graph[now]) // 骚操作,c++11 基于范围的 for 循环,遍历容器十分方便
if (dis[now] + x.second & dis[x.first]) // 松弛
dis[x.first] = dis[now] + x.
nbs.push(x.first);
nbs.pop();
for (int i(1); i &= N; ++i)
printf("%d ", dis[i]);
看我写代码那么铺张浪费,是不是还很短啊:)
虽说是dijkstra的模板,但单纯的dijkstra是过不了的,这里需要用的STL的优先队列,说白了就是堆优化的dijkstra,虽说看到楼下已有大神用了这种优化,但并不是说的很明白,所以我来补充下对堆优化的dijkstra算法的说明。
首先是需要一个pair,用来放从源点到指定点的距离和指定点,然后见一个pair类型的优先队列(最小堆),长度短的就在队首,所以接下来有点像用队列优化spfa,当队列不为空时,依次取出没有确定的点,通过该点进行松弛,这就是dijkstra了。大体上就是这个思想。
附上AC代码,也是一个很常用的模板。
#include&iostream&
#include&cstdio&
#include&queue&
const int MAX_E = 500010, MAX_P = 10010, INF = ;
typedef pair&int, int&
priority_queue&pii, vector&pii&, greater&pii& &
struct edge {
} e[MAX_E];
int head[MAX_P], dis[MAX_P], tot, n, m,
bool vis[MAX_P];
void add(int a, int b, int c) {
e[++tot].v =
e[tot].w =
e[tot].next = head[a];
void init() {
for (i = 1; i &= i++) {
head[i] = -1;
dis[i] = INF;
dijkstra(int s) {
dis[s] = 0;
q.push(make_pair(dis[s], s));
while (!q.empty()) {
pii tmp = q.top();
int x = tmp.
if(vis[x])
for(i = head[x]; i + 1; i = e[i].next) {//i+1就是i!=-1,因为head数组初始化为-1
if(dis[e[i].v] & dis[x] + e[i].w) {
dis[e[i].v] = dis[x] + e[i].w;
q.push(make_pair(dis[e[i].v], e[i].v));
int main() {
scanf("%d%d%d", &n, &m, &s);
int i, f, g,
for (i = 1; i &= i++) {
scanf("%d%d%d", &f, &g, &w);
add(f, g, w);
//add(g, f, w);双向图时用的
dijkstra(s);
for (i = 1; i &= i++)
printf("%d ", dis[i]);
单元最短路一般有两种写法
SPFA和dijkstra
个人喜欢后者(虽然好像写法和时间复杂度都不怎么优)。
一开始我没看数据,裸的dij就拍上去了。
dijkstra需要堆优化
这里使用STL一个叫priority_queue的神(zhe)奇(xue)的数据结构。
里面跑的就是一个堆。
它支持用struct修改排序规则。
详细看代码。
下面70分代码
#include&cstdio&
#include&algorithm&
#include&queue&
const int inf=;
struct edge{
int to,next,s;
}e[500005];
int n,m,sta,
int head[10005];
int dis[10005];
bool vis[10005];
void dijkstra(int sta){
for(int i=1;i&=n;i++){
dis[sta]=0;
for(int k=1;k&n;k++){
for(int i=1;i&=n;i++){
if(!vis[i]&&dis[i]&mn){
mn=dis[i];
vis[now]=1;
int c=head[now];
int mu=e[c].
if(dis[mu]&dis[now]+e[c].s)dis[mu]=dis[now]+e[c].s;
int main(){
scanf("%d%d%d",&n,&m,&sta);
int u,v,c;
for(int i=1;i&=m;i++){
scanf("%d%d%d",&u,&v,&c);
e[i].to=v;
e[i].next=head[u];
head[u]=i;
dijkstra(sta);
for(int i=1;i&=n;i++){
printf("%d ",dis[i]);
#include&cstdio&
#include&algorithm&
#include&queue&
const int inf=;
struct edge{
int to,next,s;
}e[500005];
struct point{
}dis[10005];
struct cmp1{
bool operator ()(point &a,point &b){
return a.s&b.s;
priority_queue&point,vector&point&,cmp1&
int n,m,sta,
int head[10005];
bool vis[10005];
void dijkstra(int sta){
for(int i=1;i&=n;i++){
dis[i].na=i;
dis[sta].s=0;
int now=0;
q.push(dis[sta]);
for(int k=1;k&n;k++){
while(vis[now]){
now=q.top().
vis[now]=1;
int c=head[now];
int mu=e[c].
if(dis[mu].s&dis[now].s+e[c].s){
dis[mu].s=dis[now].s+e[c].s;
q.push(dis[mu]);
int main(){
scanf("%d%d%d",&n,&m,&sta);
int u,v,c;
for(int i=1;i&=m;i++){
scanf("%d%d%d",&u,&v,&c);
e[i].to=v;
e[i].next=head[u];
head[u]=i;
dijkstra(sta);
for(int i=1;i&=n;i++){
printf("%d ",dis[i].s);
SPFA感觉非常简单又好懂
开一个队列,先入先出,不断拿队头的元素松弛,松弛成功的话把对象丢进队列(在队里就省略进队操作)
效率迷之高
#include&cstdio&
#include&cstring&
#define inf
struct edge{
int to,next,s;
}e[500005];
int h[10005],
int dis[10005];
int q[],tail,
bool inq[10005];
void add(int u,int v,int s){
e[++tot].to=v;
e[tot].next=h[u];
e[tot].s=s;
void SPFA(){
for(int i=1;i&=n;i++){
dis[st]=0;
q[tail++]=
inq[st]=1;
while(head&tail){
int u=q[head++];
for(int c=h[u];c;c=e[c].next){
int v=e[c].
if(dis[u]+e[c].s&dis[v]){
dis[v]=dis[u]+e[c].s;
if(!inq[v]){
q[tail++]=v;
int main(){
scanf("%d%d%d",&n,&m,&st);
for(int i=1;i&=m;i++){
int u,v,s;
scanf("%d%d%d",&u,&v,&s);
add(u,v,s);
for(int i=1;i&=n;i++){
printf("%d ",dis[i]);
SPFA真迷……
作为一个模板题,很显然应该写普适性的模板。
同时包含了分支限界,Dijkstra 以及 Bellman Ford 算法。
另外,Dij朴素40,BmF 不判断松弛完毕的话70,这体现了算法复杂度相等(相似)的情况下,压低常数的重要性。
代码如下,为了提高普适性,大量使用了预处理命令(代码说明见代码注释)
CPP(基本上是 C 的架构)
#include &cstdio&
#include &cstdlib&
#include &cstring&
#define min(a,b) (((a)&(b))?(a):(b)) // 定义min
//#define USING_DIJ // 是否编译 Dijkstra 算法
#define USING_BMF // 是否编译 Bellman Ford 算法
//#define USING_BAB // 是否编译 分支限界 算法
#ifdef USING_BAB // 如果使用分支限界
#define tagBRANCH_DEF // 是否编译 分支结构 结构体
typedef struct tagLINK // 边
typedef LINK* PLINK ;
#ifdef tagBRANCH_DEF
typedef struct tagBRANCH // 分支
typedef BRANCH* PBRANCH;
typedef int* PINT ;
typedef bool* PBOOL ;
#ifdef USING_DIJ
PINT Dijkstra(const int N,const int M,const int Start,PLINK links); // 40
// 用法是 创建一个 int型的指针 , 令其等于 Dijkstra(const int N,const int M,const int Start,PLINK links)
// 参数分别为 N : 点的数量 , M:边的数量,Start:开始节点,links:所有边构成的数组
#ifdef USING_BMF
PINT BellmanFord(const int N,const int M,const int Start,PLINK links); // 70
// 用法 同Dij , 只是函数名不同
#ifdef USING_BAB
PINT Branch_and_Bound(const int N,const int M,const int Start,PLINK links); // 40
// 用法 同Dij , 只是函数名不同
void BAB_EnQuene( PBRANCH& quene , BRANCH state , int& begin , int& end , int& size );//BAB辅助函数
BRANCH BAB_DeQuene( PBRANCH quene , int& begin , int& end
, int& size );// BAB辅助函数
int link_cmp(const void* A,const void* B){PLINK a=(PLINK)A,b=(PLINK)B;return a-&W - b-&W;} // 边权升序排序
int main()
int N,M,S;
scanf("%d%d%d",&N,&M,&S);
S -- ; // 将 S 从 1~N 重置为 0 ~ N-1
PLINK links = (PLINK)calloc(M,sizeof(LINK));
for(int i=0;i&M;i++)
scanf("%d%d%d",&links[i].F,&links[i].G,&links[i].W); // 读入边
links[i].F -- ;// 将 起点 从 1~N 重置为 0 ~ N-1
links[i].G -- ;// 将 终点 从 1~N 重置为 0 ~ N-1
qsort(links,M,sizeof(LINK),link_cmp); // 权重升序
PINT ans = BellmanFord(N,M,S,links); // 调用BMF 生成答案数组 (如果使用其它算法,请更改调用函数的函数名)
for(int i=0;i&N;i++)printf("%d%c",ans[i],((i&N-1)?0x20:0x0A)); // 输出结果
free(ans); // 释放内存
#ifdef USING_DIJ
PINT Dijkstra(const int N,const int M,const int Start,PLINK links) // dij 算法
PINT Distance = (PINT)calloc(N,sizeof(int)); // 初始化最短距离数组
PINT dis = D // 创建最短距离数组的别名 dis
for(int i=0;i&N;i++)dis[i] = 0x7FFFFFFF; // 初始化为 INT_MAX
dis[Start] = 0 ;
PBOOL vis = (PBOOL)calloc(N,sizeof(bool));
vis[Start] =
for( int SizeofS = 1 ; SizeofS & N ; SizeofS ++ ) // 最多N-1次
int best = 0 , MinL = 0x7FFFFFFF ;
// 最佳选择和最短路长度
for(int i=0;i&M;i++)
if( vis[links[i].F] == true && vis[links[i].G] == false )
if( MinL & links[i].W + dis[links[i].F] )
best = links[i].G ;
MinL = links[i].W + dis[links[i].F] ;
if( vis[best] )
vis[best] =
dis[best] = MinL ;
free(vis);
#ifdef USING_BMF
PINT BellmanFord(const int N,const int M,const int Start,PLINK links)
// 初始化部分同理
PINT Distance = (PINT)calloc(N,sizeof(int));
PINT dis = D
for(int i=0;i&N;i++)dis[i] = 0x7FFFFFFF;
dis[Start] = 0 ;
for(int i=0;i&N;i++)
bool changed = // 本轮操作有意义
for(int j=0;j&M;j++)
if( dis[links[j].F] & 0x7FFFFFFF && dis[links[j].G] & dis[links[j].F] + links[j].W ) // 若起点被访问且可以有更短的路径
dis[links[j].G] = dis[links[j].F] + links[j].W; // 更新最短距离
if( changed == false ) //
判断是否行为有意义。不加这句70 , 加上 AC
#ifdef USING_BAB
PINT Branch_and_Bound(const int N,const int M,const int Start,PLINK links) // 分支限界 (自己好像没有理解其精髓,写的类似于BFS)
// 初始化部分同理
PINT Distance = (PINT)calloc(N,sizeof(int));
PINT dis = D
for(int i=0;i&N;i++)dis[i] = 0x7FFFFFFF;
dis[Start] = 0 ;
PINT chart_width = (PINT)calloc(N,sizeof(int)); // 邻接链表的宽度
PLINK** chart = (PLINK**)calloc(N,sizeof(PLINK*)); // 邻接链表
for(int i=0;i&N;i++)chart[i] = (PLINK*)calloc(M,sizeof(PLINK)); // 分配邻接链表空间
for(int i=0;i&M;i++)
chart[links[i].F][chart_width[links[i].F]] = links +
chart_width[links[i].F] ++ ;
PBRANCH quene = (PBRANCH)calloc( N*N ,sizeof(BRANCH));
int quene_begin = 0 , quene_end = 0 , quene_size = N*N ;
for(int i=0;i&chart_width[Start];i++)BAB_EnQuene( quene , (BRANCH){chart[Start][i]-&W,chart[Start][i]-&G} , quene_begin , quene_end , quene_size );
while( quene_begin != quene_end ) // 当队列中有元素
BRANCH state = BAB_DeQuene( quene , quene_begin , quene_end , quene_size ); // 出队
if( state.dis &
dis[state.s] ) // 如果小于当前最短距离
dis[state.s] = state.
for(int i=0;i&chart_width[state.s];i++)
if( state.dis + chart[state.s][i]-&W & dis[chart[state.s][i]-&G] )
BRANCH temp = { state.dis + chart[state.s][i]-&W , chart[state.s][i]-&G };
BAB_EnQuene( quene , temp , quene_begin , quene_end
, quene_size );
for(int i=0;i&N;i++)free(chart[i]); // 释放所有空间
free(chart);
free(chart_width);
free(quene);
void BAB_EnQuene( PBRANCH& quene , BRANCH state , int& begin , int& end , int& size ) // 入队
quene[end] =
end = (end+1) %
BRANCH BAB_DeQuene( PBRANCH quene , int& begin , int& end
, int& size ) // 出队
BRANCH temp = quene[begin];
begin = (begin+1)%
STL版Dijkstra
花了好长时间才AC了,不容易啊TAT
这次的题解使用了一些比较好用的模板,初学STL的同学可以拿来练练手:
priority_queue
pair(一个奇怪的东西。。。
算法原理我就不多说了,下面贴代码
#include&cstdio&
#include&cstdlib&
#include&cstring&
#include&algorithm&
#include&iostream&
#include&cmath&
#include&utility&
#include&queue&
#include&vector&
#define Heap pair&int,int&
#define Inf
struct data {
}edge[500005];
vector&int& poin[10005];
int s,n,m;
int v[10005];
int main()
scanf("%d%d%d",&n,&m,&s);
for(i=1;i&=m;i++) {
scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].w);
poin[edge[i].from].push_back(i);
for(i=1;i&=n;i++) v[i]=I
priority_queue& Heap,vector&Heap&,greater&Heap& & Q;
Q.push(make_pair(0,s));
while(!Q.empty()) {
Heap N=Q.top();
if(v[u]!=N.first)
for(int i=0;i&poin[u].size();++i) {
data e=edge[poin[u][i]];
if(v[e.to]&v[u]+e.w){
v[e.to]=v[u]+e.w;
Q.push(make_pair(v[e.to],e.to));
for(i=1;i&=n;i++) printf("%d ",v[i]);
单源最短路,看了看数据N&=10000,M&=500000用bellman——ford会超时,单纯的用dijkstra也要超时,先讲一下dijkstra算法的思想;dijkstra算法包括两个步骤:1找到最短距离已经确定的点。2从这个点出发确定相邻的点的最短距离。在最开始只有s——s的最短路径是确定的,所以先从s开始,那么如果模拟出来,定义d[i]表示从s到i号顶点的最短距离。
#include&cstdio&
#include&iostream&
#define ll long long
#define INF
int n,m,s;
ll cost[1];
ll d[10010];
bool used[10010];
void dijkstra(int s){
fill(d,d+n+1,INF);
fill(used,used+n+1,false);
for(int u=1;u&=n;u++)
if(!used[u]&&(v==-1||d[u]&d[v])) v=u;
for(int u=1;u&=n;u++)
d[u]=min(d[u],d[v]+cost[v][u]);
int main(){
for(int i=0;i&=10001;i++)
for(int j=0;j&=10001;j++) if(i!=j) cost[i][j]=INF;
cin&&n&&m&&s;
for(int i=1;i&=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
cost[u][v]=w;
dijkstra(s);
for(int i=1;i&=n;i++) printf("%lld ",d[i]);
这个程序复杂度是v*v的很明显对于题目中的数据范围要超时的,想想怎么优化,取出最小值。。。。想到什么了,小顶堆,对,可以用小顶堆维护最短路径,用邻接表来维护边,那么复杂度就变成了ElogV,下面是代码,简单易懂;
#include&cstdio&
#include&cstring&
#include&iostream&
#include&queue&
#include&vector&
#include&algorithm&
#define ll long long
#define INF
int n,m,s,head[50010],
ll d[10010];
bool used[10010];
struct edge{
int to,next,w;
}es[500010];
void addedge(int u,int v,int w){
es[ecnt].to=v;
es[ecnt].next=head[u];
es[ecnt].w=w;
head[u]=ecnt++;
}//邻接表存边。
typedef pair&int,int& P;//用于存到达点的最短路径
void dijkstra(int s){
priority_queue&P,vector&P&,greater&P& &//按照P第一个元素从小到大取出的队列
fill(d,d+n+1,INF);
fill(used,used+n+1,false);
q.push(P(0,s));
while(!q.empty()){
P p=q.top();q.pop();
if(used[u])
int pp=head[u];
while(pp!=-1){
int v=es[pp].
if(!used[v]&&d[v]&d[u]+es[pp].w){
d[v]=d[u]+es[pp].w;
q.push(P(d[v],v));
pp=es[pp].
int main(){
memset(head,-1,sizeof(head));
cin&&n&&m&&s;
for(int i=1;i&=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
dijkstra(s);
for(int i=1;i&=n;i++) printf("%lld ",d[i]);
putchar('\n');
总结:没什么总结的,这个是dijkstra算法的模板。
用二维数组会爆!!!数据太大了,可以用动态数组+结构体。
#include&cstdio&
#include&vector&
//int pre[19999];
bool flag[19999];//蓝白点
int du[];//队列
struct turr{
vector &turr& w[10009];
int dis[19999];
int main()
scanf("%d%d",&n,&m);
scanf("%d",&v);
for(int i=1;i&=m;i++)
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
e1.cost=z;
w[x].push_back(e1);//放入动态数组
flag[v]=1;
for(int i=1;i&=n;i++)
du[1]=v;dis[v]=0;
int head=0,tail=1;//初始化
int o=du[head];
flag[o]=0;
for(int i=0;i&w[o].size();i++)
int t=w[o][i].
if(dis[t]&dis[o]+w[o][i].cost)
dis[t]=dis[o]+w[o][i].
if(!flag[t])
flag[t]=1;
du[++tail]=t;
while(head&tail);//spfa算法
for(int i=1;i&=n;i++)
printf("%d ",dis[i]);
有一些人说SPFA暴死TLE,但我全AC了呀...
设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
#include &iostream&
#include &cstdio&
#include &cmath&
#include &queue&
#include &vector&
#include &string.h&
#define uint unsigned int
#define ll long long
#define ull unsigned ll
#define pii pair&int,int&
#define pb push\_back
#define mp make\_pair
#define INF
#define LINF
#define ms(l) memset(l,0,sizeof(l))
uint dis[10001];
class Edge{
vector&Edge& data[10000];
void addedge(uint fr,uint to,uint co){
e1.to = e1.cost =
data[fr].pb(e1);
void spfa(void){
queue&uint&
bool at[10001];
q.push(spoint);
for(uint i = 1;i &=i++)
dis[i] = INF;
dis[spoint] = 0;
at[spoint] = 1;
while(!q.empty()){
u = q.front();
at[u] = 0;
for(uint i = 0;i & data[u].size();i++)
if(dis[u]+data[u][i].cost & dis[data[u][i].to]){
dis[data[u][i].to] = dis[u]+data[u][i].
if(!at[data[u][i].to]){
at[data[u][i].to] = 1;
q.push(data[u][i].to);
int main(){
//freopen("i.txt","r",stdin);
cin && n && m &&
for(uint i = 1;i &=i++){
uint f,t,c;
cin && f && t &&
addedge(f,t,c);
for(uint i = 1;i &=i++)
cout && dis[i] && ' ';
SPFA的优化
SPFA存在两种优化方式,一种是SLF(Small Label First)优化,另外一种为LLL(Large Label Last)优化。个人感觉SLF好理解一些;
SLF具体操作
设队首元素为 i,队列中要加入节点 j,在 dj&=di 时加到队首而不是队尾,否则和普通的 SPFA 一样加到队尾。
在本身有读入优化的前提下,由677ms优化为406ms
#include &cstdio&
#include &limits.h&
#include &iostream&
#include &algorithm&
const int maxne = 600001;
const int maxnn = 20001;
const unsigned long long int inf = ;
int n,e,s,t,
int last[maxne],q[maxne],check[maxnn];
long long dis[maxnn];
bool is[maxnn],
int read()
int x=0,f=1; char ch=getchar();
while(ch&'0'||ch&'9') {if(ch=='-') f=-1; ch=getchar();}
while(ch&='0'&&ch&='9') {x=x*10+ch-'0'; ch=getchar();} return x*f;
struct line {int to,next,v;}l[maxne];
void add(int u,int v,int w) { l[++cnt].to=v; l[cnt].next=last[u]; last[u]= l[cnt].v=w; }
void spfa(int a)
for(int i=1;i&=n;i++) dis[i]=
dis[a]=0; is[a]=1; q[0]=a; check[a]++;
int head=0,tail=1;
while(head!=tail)
int now=q[head++];
if(head==n+1) head=0;
for(int i=last[now];i;i=l[i].next)
if( dis[now]+l[i].v&dis[l[i].to] && dis[now]!=inf)
dis[l[i].to]=dis[now]+l[i].v;
if(!is[l[i].to])
is[l[i].to]=1;
if(dis[l[i].to]&dis[q[head]])
head--; if(head==-1) head=n;
q[head]=l[i].
check[l[i].to]++;
if(check[l[i].to]==n) { fuhuan=1;}
q[tail++]=l[i].
if(check[l[i].to]==n) { fuhuan=1;}
if(tail==n+1) tail=0;
is[now]=0;
int main()
int u,v,w;
n=read();e=read();
for(int i=1;i&=e;i++)
u=read();v=read(); w=read();
add(u,v,w);
for(int i=1;i&=n;i++)
if(dis[i]==inf) cout&&" ";
else cout&&dis[i]&&" ";
pascal的题解不是很多 贡献一个 注意 更新距离是不用理会节点是否在队列里面 是否入队是要判断是否在队列里面 。。不然只有十分
type s1=record
go,next,w:
head:array[1..10000] //head:=-1
edge:array[1..500000] of s1;
dis:array[1..10000]
dl:array[1..1000000]
ju:array[1..10000]
n,m,s,cnt,fi,gi,wi,loop:
procedure add(f,t,we:longint);
edge[cnt].go:=t;
edge[cnt].w:=
edge[cnt].next:=head[f];
procedure spfa(h:longint);
f,t,loop,visit,cur:
dis[h]:=0;
while f&=t do
visit:=head[dl[f]];
while visit&&-1 do
cur:=edge[visit].
((dis[dl[f]]+edge[visit].w)&dis[cur]) then** 注意此处 不用 ju数组判断
dis[cur]:=dis[dl[f]]+edge[visit].w;
### if not ju[cur] then 这里要
visit:=edge[visit].
ju[dl[f]]:=
readln(n,m,s);
fillchar(ju,sizeof(ju),false);
for loop:=1 to n do
head[loop]:=-1;
for loop:=1 to n do
dis[loop]:=;
for loop:=1 to m do
read(fi,gi,wi);
add(fi,gi,wi);
for loop:=1 to n do
write(dis[loop],' ');
感觉写了一个年度最丑的 SPFA算法
用邻接表存储边,冗余稍大,但感觉还是不错的……吧
第一次提交队列开小了,还挂了三个点-.-
#include &iostream&
const int Infinity = ;
struct Side{
int dist[11111];
//光棍节,多来几个1
Side* s[51111];
int queue[111111];
int main()
cin && n && m &&
for (i = 1; i &= i++){
s[i] = new S
s[i]-&src = s[i]-&des =
s[i]-&dist = 0;
s[i]-&next = NULL;
dist[i] = I
dist[st] = 0;
int src,des,
for (i = 0; i & i++){ //有向边
cin && src && des &&
tmp = new S
tmp-&src =
tmp-&des =
tmp-&dist =
//if (s[src]-&next != NULL)
tmp-&next = s[src]-&
//tmp-&next = NULL;
s[src]-&next =
queue[qs] =
while (qs &= qt){
src = queue[qs++];
side = s[src]-&
while (side != NULL){
if (dist[side-&des] & dist[src]+side-&dist){
dist[side-&des] = dist[src]+side-&
queue[qt] = side-&
side = side-&
for (i = 1; i &= i++)
cout && dist[i] && " ";
206ms的最快最短路,不知道有没有后来者能打败它。
思路:Bellman-ford(你要说是SPFA也行)的优先队列优化。思路很简单,让离原点较近的点先开始进行松弛,这样的点松弛的性价比更高,不会出现太多反复松弛、松弛一遍还不够的点。优先队列方面,我没有手写,但也没有用STL的那个,太慢了。我用的是pb_ds的配对堆,速度超快,很多操作都是O(1)的,常数也很小。
#include&cstdio&
#include&ext/pb_ds/priority_queue.hpp&
using namespace __gnu_
char Buf[1&&25],*buf=B
int getint()
int x=0,f=1;
while(*buf&'0'||*buf&'9')if(*buf++=='-')f=-f;
while(*buf&='0'&&*buf&='9')x=x*10-'0'+*buf++;
return x*f;
const int N=10005,M=500005,INF=;
int n,m,s,Next[M],Head[N],Point[M],Weight[M],d[N];
struct Compare
__inline__ __attribute((always_inline)) bool operator()(int a,int b)//我以前的题解提到过的强制inline的方法
return d[a]&d[b];
};//自定义比较器
bool inq[N];
int main()
fread(Buf,1,sizeof(Buf),stdin);
n=getint();m=getint();s=getint();
for(int i=1,u,v,w;i&=m;i++)
u=getint();v=getint();w=getint();
Next[i]=Head[u];Head[u]=i;Point[i]=v;Weight[i]=w;
for(int i=1;i&=n;i++)d[i]=INF;
priority_queue&int,Compare&q; q.push(s); d[s]=0; inq[s]=1;
while(!q.empty())
int i=q.top(); q.pop(); inq[i]=0;
for(int e=Head[i];e;e=Next[e])
if(d[Point[e]]&d[i]+Weight[e])
d[Point[e]]=d[i]+Weight[e];
if(!inq[Point[e]]){inq[Point[e]]=1;q.push(Point[e]);}
for(int i=1;i&=n;i++)printf("%d ",d[i]);
用的是SPFA 还有算法核心 (没错 就是怕你们抄==其实是我懒得加了)
1 算法简介
SPFA(Shortest Path Faster Algorithm)是Bellman-Ford算法的一种队列实现,减少了不必要的冗余计算。也有人说SPFA本来就是Bellman-Ford算法,现在广为流传的Bellman-Ford算法实际上是山寨版。
2 算法流程
SPFA算法采用了一个队列来进行维护和实现:
(1)初始时,将源点加入队列。
(2)每次从队列中(队首)取出一个元素,并对所有与该队首顶点相邻的点进行松弛(“松驰”的含义与上面Bellman-Ford算法中描述的一样)。若某个相邻的点松弛成功,则将其入队(加入队尾)。
(3)一直对队列进行操作,直到队列为空时算法结束。
简单地说,这个算法就是利用队列优化过的Bellman-Ford算法,是利用了每个点的更新次数不会太多这一特点而发明的算法。
SPFA可以在O(kE)的时间复杂度内,求出源点到其他所有点的最短路径,并且可以处理负边。
SPFA的实现甚至比Dijkstra或者Bellman-Ford还要简单:
设Dist[i]代表源点S到任一顶点 i 的当前最短距离,Fa代表S到 i 的当前最短路径中,i 点之前的一个点的编号。开始时,Dist全部为+∞,只有Dist[S]=0,Fa全部为0。
维护一个队列,里面存放所有需要进行迭代的点。初始时,队列中只有一个点S。此外,再用一个布尔数组,记录每个点当前是否在队列中。
每次迭代时,取出队首的点v,依次枚举从v出发的边v-&u:设该边的长度为len,判断Dis

我要回帖

更多关于 跪求大佬 的文章

 

随机推荐