POJ2387-Til the Cows Come Home
开始刷邝斌最短路专题
可用平台 (评分是真的不准)
poj2387 官网又上不去了
题目有点多,保证AC人数从高到低刷个8道就行了
用搜索来弄的话,
遍历T找1,
再遍历T找2,如果有T个2,T组数据都压入队列
遍历这T组数据找3
...
总共需要遍历N次,最后找出min的路
复杂度是T^N,(2*10^3)^(10^3),学习(回顾)新算法,这博客(有迪杰斯特拉不能处理负边的例子)说错误很多,但acwing?先放着吧
回顾图的存储
之前在HIT活动中心特意写过迪杰斯特拉的题解,忘了也不想看,现学其他人的
先了解图的存储(没看后面的以图存BFS/DFS),进而看迪杰斯特拉,但感觉代码思路怎么是先把所有点都赋值一遍?而且最开始有个 dist[t] t是-1?写博客代码这么不用心吗?而且也没图,不好理解,
又看了一篇博客,有点了解了但很朦胧,没C++代码,而且他的例子真不恰当,把贪心牵扯进的话,那苹果和吃自助的例子都需要考虑后面的情况对当前造成的影响,又有个词“无后效性”,动归,此时脑瓜子又库库冒出一堆疑问(比如比搜索好在哪),还没回顾到那些,先搁置。不管咋说这个迪杰斯特拉也是仅仅保证当前最佳吧,扯远了,感觉不太对头,先看其他的。
参考博客,虽然没C++代码,但图解足够清晰!
哎,回顾个这么简答的算法,脑子锈住了,回顾了一下午还没理解透彻,邝斌的鼓励“人一我十,人十我百”,菜逼就要多锤炼
发现每篇博客都有个很重要却千篇一律的描述 "距离最短的顶点k",又找了几篇
发现了这个博客更加了解了一些(虽然我很菜但他文章最后一句“照着打一遍”,其实只是对于小小白可以这样,基本不建议照着打,时间久了只能回忆起那个人的代码风格完全记不住代码思路,不过这人的迪杰斯特拉的讲解是目前看到最清晰详细的。看完博客里的代码知道思路就开始自己写,算法原理弄清之前不要开始写,开始写之后一点都不要再看别人的代码,每一个坑踩过才行)
迪杰斯特拉就一个地方不懂,也是最关键的,为啥每次都是选此时局面的最短路径作为最优解的一部分,这一定是全局最优解必经之路么?根本没道理啊,就比如,为啥先加2进来,而不先把1加进来呢,0到1,权重是5,也是对1来说的最短的啊
我先把上面这个博客里的图拿出来捋顺下思路(把这篇文章的链接开两个页面,标签页拖出来,win+←,分屏,一个看图,一个看下面思路叙述)
如果想知道顶点0到6这个点的最短路径,其实只要你给我0到5的最短路x,和0到4的最短路y就行,我自己比较x+3和y+7即可。有点动归的意思但暂时不想牵扯那么多没回顾的知识,就他妈的说最短路。
回到上面黑色粗体的,为啥每次要选则此时路径里的最优,我看所有博客都是先把最短的2加进来,其实我觉得跟之前说的一样,什么东西都要抓重点,而不是无脑开好大数组来规避可能出现的问题从而AC掉却也不清楚开数组问题上可能出现的真正错误点,或者无脑写一堆模板,这里也是,去掉没用的外壳,做个控制变量,把最关键的抽丝剥茧点出来,做个约束,才更好让人理解
上面博客和全网的思路,用在这个图上,都是先把2加进来。但我偏偏要先把1加进来
找0到所有点的最短路径,先做到此时最优,即贪心(但只是伪最优),比如0到2和0到1的最短分别为2和5,那随便加哪个进去都行,不用非得是最短的2进,我偏偏要加1进来(即已确定0到1的权重最短为5,不会再有更优的了),因为没有比0→1能更近到达1的点了,然后0到3,0到4更新为6和11,0到2初始数据是2
那么此时局面是,
0到2伪最短2
0到3伪最短6
0到4伪最短11
0到1真最短5
此时按照其他博客思路是应该先加最短的,那如果到3最短就先加3吗?放屁!理论上来说如果013最短,2这个亲儿子都没加进来,证明啥?
证明0到2简称02,02比013都远,那023只会更远,023更加比013远,对吧?那2先不用掺乎进来了,3就可以去摆平其他的路线,以最小的代价。
那如果3到5是很大的数,这时候0135这条路因为3到5很大就先停这了,回头去看2,之前说02比013远,但如果没远太多,且2到5也很小,这时候025不就比0135更优秀了吗!那5这个点就会用025这条路,用来后面的决策。
现在回过头说刚才放屁那,先加进3的问题,再回顾下此时局面,0到1最短路确定是5,加了进来,0到2/3/4都是伪的,分别为2/6/11,其实3如果是此时最优,的确实应该加进来作为已确定的,之后看0到245,但为了说明算法真正思路,这里不加3,而是加2,比如0到2的距离,比013远,但也要先加2,意思是想说,把所有到3的点(入度),都算出来,再把3加进去,而不是看谁此时路径最短就加谁进去。
2加完了现在更新3和5,0到3是8但之前是6不更新,0到5没有过,更新为10
此时局面
0到1最短5已确定
0到2最短2已确定
0到4是伪最短11
0到3是伪最短6
0到5是伪最短10
此时出现的差别先说一下,其他博客都是一直取最短放进已有数据集
我是找所有指向他的点,即入度,找完才放进数据集
此时我找入度都找完的点即3,能到他的点都走完了,最小已经定了就是013为6,那3加进来,更新45,4变为7,5变为8,
此时局面
0到1最短是5已确定
0到2最短是2已确定
0到4是伪最短7
0到3是伪最短6
0到5是伪最短8
此时按照其他博客就是最短的7,即0到4加进来,但我思路是看度,4和5入度都判断完了,我随便哪个都行,我偏偏要去加0到5进来,再更新6,之前没有过,变为11,4和5存的也因入度都判断完了变成真最短
此时局面
0到1最短路是5已确定
0到2是2已确定
0到4是7伪最短
0到3是6已确定
0到5是8已确定
0到6是11伪最短
随后再加4进来,更新6,是14,比11大,不更新
0到1最短路是5已确定
0到2是2已确定
0到4是7真最短
0到3是6已确定
0到5是8已确定
0到6是11真最短
到这我发现了,答案都一样
我的方法最基本朴素思路最简单,是要看度,但存图只有出度没存入度。但迪杰斯特拉不考虑入度,直接看最短的就比我的思路好很多。其实原理是一样只是避开的入度,且这样能抽丝剥茧出真正的最短路思想:刷新!!每次刷新都会更新成更短的也叫松弛操作,所以每次找最近的不是必要的,找哪个点都行,之前我是这样说的,但现在发现,这里找哪个点都行,必须要是度都为0的里面,找哪个加入都行,但没法存入度就只能找最短了,这样既然省去了看入度的操作,也保证了此时点就是顶点到这都最优路径,加入数据集,再用这个点来松弛一遍他能到的点,被松弛的这些点作为此局面的伪最优,若干次伪最优之后,一次次更新一次次调教,会出现真最优。伪最优一定是往真最优方向发展的
跟之前反转灯泡问题 Fliptile ,还有数论素数筛一样,从最朴素暴力到最优解,有个推导过程才会理解最优解的思想
其实这个图把0到2权设成7更好理解,这样1点加完,找最短就加3,为什么加3?不考虑另一个入度吗?
0到1真最优5已确定
0到2是7初始值
1加入导致的更新3,013是6伪
1加入导致的更新4,014是11伪
此时确定好了1,接下来从234中考虑,我已经可以确定0到3的距离6就是最短,没必要再看2和4了,他俩等我打5的时候有难再让他俩出兵(上面有例子)。至于为啥可以确定0到3的6是最短,因为起初0到所有挨着的点都赋值了,0到2的7,再加上2到3(必须是正数,无论权是什么正数),都一定比7大,不可能更优了
简单来说就是,起点到某点是目前几个值里的最小值,那一定就是起点到这个点的最短路因为其他没加入的说明是无穷大,已经加入了的,那些点都还没到这呢,都比我这个大,那再经过其他路线到我这,则会更大。所以,只要找到此时最短的距离,就可以确定是顶点到这个点的最短路径,确定完一个最短路径其他所有点沾亲带故的就都更新一下,使得到顶点更近
尝试看代码,这些人真的刷过题吗?还是只是纸上谈兵,所有人博客都有这一句 if(!vis[j] && (node == -1 || dis[j]<dis[node])) 妈的一开始node是-1啊。艹这群傻逼。真难怪我上面说,一点没说冤枉他,我都说轻了,抄代码屁用没有,抄错都不知道。之前看过关于数组下标大佬博客,负数数组下标搁置先
无奈搜了个百度的迪杰斯特拉C语言代码(严蔚敏口碑极差,下面严蔚敏版本看都懒得看,一坨屎一样),真棒,百度在找题的其他平台,报错和新算法代码示例的时候很给力
1 #include<stdio.h> 2 #include<stdlib.h> 3 #define max1 10000000 //原词条这里的值太大,导致溢出,后面比较大小时会出错 4 int a[1000][1000]; 5 int d[1000];//d表示源节点到该节点的最小距离 6 int p[1000];//p标记访问过的节点 7 int i, j, k; 8 int m;//m代表边数 9 int n;//n代表点数 10 int main() 11 { 12 scanf("%d%d",&n,&m); 13 int min1; 14 int x,y,z; 15 for(i=1;i<=m;i++) 16 { 17 scanf("%d%d%d",&x,&y,&z); 18 a[x][y]=z; 19 a[y][x]=z; 20 } 21 for( i=1; i<=n; i++) 22 d[i]=max1; 23 d[1]=0; 24 for(i=1;i<=n;i++) 25 { 26 min1 = max1; 27 //下面这个for循环的功能类似冒泡排序,目的是找到未访问节点中d[j]值最小的那个节点, 28 //作为下一个访问节点,用k标记 29 for(j=1;j<=n;j++) 30 if(!p[j]&&d[j]<min1) 31 { 32 min1=d[j]; 33 k=j; 34 } 35 //p[k]=d[k]; // 这是原来的代码,用下一 条代码替代。初始时,执行到这里k=1,而d[1]=0 36 //从而p[1]等于0,这样的话,上面的循环在之后的每次执行之后,k还是等于1。 37 p[k] = 1; //置1表示第k个节点已经访问过了 38 for(j=1;j<=n;j++) 39 if(a[k][j]!=0&&!p[j]&&d[j]>d[k]+a[k][j]) 40 d[j]=d[k]+a[k][j]; 41 } 42 //最终输出从源节点到其他每个节点的最小距离 43 for(i=1;i<n;i++) 44 printf("%d->",d[i]); 45 printf("%d\n",d[n]); 46 return 0; 47 }
第一次很深刻,以后估计就是这个写法了
回顾完迪杰斯特拉看这个题(发现两个专题第一题都跟牛有关)
这个题说是双向奶牛轨道,感觉会有这种数据吧?脚丫子想都知道
3 5 10
5 3 90
5 3 40
先处理下,处理成数据只有3 5 10再进行最短路算法
憋了半天写出的代码,简简单单直接AC
AC代码
1 //N(1000)个点 2 //T(2000)条trail 3 //每条的权最大100 4 5 #include<stdio.h> 6 #include<iostream> 7 #include<string.h> 8 using namespace std; 9 int tra[1001][1001];//tra[900][1000]=100:表示地标点900到地标点1000权重是100 10 int T,N; 11 int dir[1001];//dir[1000]表示顶点到1000这个点的最短距离 12 int vis[1001]; 13 int mindir; 14 int main() 15 { 16 // freopen("zhishu.txt","r",stdin); 17 int a,b,c; 18 //此题数据从1开始不是0开始 19 while(cin>>T>>N){ 20 memset(tra,0x3f,sizeof(tra)); 21 tra[1][1]=0;//第一个(1,1)点为0即可,其他的比如(3,3)没必要,因为只有第一个点需要以0距离的身份进入,后面再进入就是互相比跟其他点的距离了.至于最后一个点用没加进来只有他这个条件就可以加进去 22 for(int i=0;i<T;i++){ 23 cin>>a>>b>>c; 24 if(c<tra[a][b]){ 25 tra[a][b]=c; 26 tra[b][a]=c;//其实这样写没必要,但防止数据只有5 3 90,但需要3 5 90这种情况.理论上来说只需要小大这个顺序的轨道 27 28 } 29 }//读取T个奶牛轨道同时进行数据的筛选 30 31 //检测读取数据对不对 32 // cout<<endl; 33 // for(int i=1;i<=N;i++) 34 // for(int j=1;j<=N;j++) 35 // if(tra[i][j]!=0x3f3f3f3f) 36 // cout<<i<<" "<<j<<" "<<tra[i][j]<<endl; 37 38 memset(dir,0x3f,sizeof(dir)); 39 memset(vis,0,sizeof(vis)); 40 dir[1]=0; 41 int p; 42 43 for(int i=1;i<=N;i++){//N个点进,N次就都进来了 44 45 mindir=0x3f3f3f3f; 46 for(int j=1;j<=N;j++){//找最小的点 47 if(vis[j]==0 && dir[j]<mindir){ 48 mindir=dir[j]; 49 p=j; 50 } 51 } 52 vis[p]=1;//p标记为1以后不用再更新这个点了 53 54 //开始松弛 55 for(int k=1;k<=N;k++){ 56 if(dir[k]>dir[p]+tra[p][k]) 57 dir[k]=dir[p]+tra[p][k]; 58 } 59 } 60 cout<<dir[N]<<endl; 61 } 62 }
看看题解博客,有没有更好的写法,学两手,结果发现好多人管这个输入叫有坑,这不读完题起手就膝跳反射,不是,条件反射么,咋会想不到
发现题解都差不多,此题over
备注:其实第43行 i<N 也行,再此引用那个抄人代码的家伙的博客中的一句话
“要寻找n-1次,找到点之后,对其邻接点进行松弛,为什么只要寻找n-1个点呢?因为当
剩下一个点的时候,这个点已经没有需要松弛的邻接点了。此时从源点到这个点的距离就是最短距离了。”
自用:
###:Adblock Plus
拦截CSDN底部关注/点赞/收藏/代码:blog.csdn.net##.left-toolbox。有时候半屏看这底部栏很挡害,但有时候又不能确定博客是不是有问题,又要取消拦截看看评论,所以拎出来记录下
拦截CSDN左边博主信息代码:blog.csdn.net##.blog_container_aside。win+←的时候就很别扭i.cnblogs.com##.left.sidebar
###:突然想到对拍是给赛场上防止WA而罚时用的,跟自己的暴力对拍
###:要专注啊!!!再也没有高中转10班后的专注时刻了
###:
1 有个总骂人被踢出去的 2 数一巨巨好友验证炼铜 3 第一届叫什么11阶梯CCCC程序设计大赛天梯赛 4 monenta计蒜客 5 毛老师:中午老师是休息的,然而我并..并不休息 6 为什么感觉poj就像没片撸一样难受 7 学吐的哈理工linlinsong阿里大佬
###: