POJ2253-Frogger
继续刷邝斌最短路专题
麻痹的POJ又开始崩溃死好几天了,不挂POJ链接了
这题也没给青蛙跳跃极限啊,如果第一次3 4点5跳不过去咋办?都默认可以跳吗??
洛谷描述有问题,其实青蛙不存在跳跃极限,但题意是说,想找到“若干次跳跃中最长的那一次的距离”最短
感觉这题不是迪杰斯特拉了吧,迪杰斯特拉是求最短路径,但这个题不要求最短路径,只要求路径中途两个点尽可能的仅。那如果一步就能到,距离是5,但有条距离为8的显然更远,但如果走8步,每步走1,这却是答案。
学学最短路其他算法吧,感觉应该用多源最短路:Floyd
找到Floyed博客但讲解的不好,三层for的顺序每次都不是最优,刷新后才是最优,但刷新顺序我没理解,按照3层for思路,必定有这步定下后当作伪最优,再走下一步基于这个伪最优,逐步更新找出所有步骤的两点之间最优解,但不明白代码为什么这么写,for的顺序应该不可以换吧。万一伪最优之前还有更优的呢
又看了这个博客,图解非常棒,且有记录路径小技巧(这人的图解,代码风格和注释,算法解释,一等一的棒,果断关注了)但还有一些小疑惑:
发现没一个真正解释清楚这个算法的,许多人都是模拟验证他的正确性,但无法解释诸多疑惑,应该从发明算法的发明人角度去推导才好理解啊
结合上面那个有棒棒图解博客里的图,进一步阐述算法思路(可以把JeffCoding这人的超链接博客同时开两个标签页,alt+←拖出来,分屏看他的图和我下面的解释)
对于所有的距离,当我更新的时候,是把点逐个加入,也就是插进来,插一个点,更新49个距离,再插第二个点,再更新49个距离...最后插入G点,更新49个距离
AA BA CA DA EA FA GA
AB BB CB DB EB FB GB
AC BC CC DC EC FC GC
AD BD CD DD ED FD GD
AE BE CE DE EE FE GE
AF BF CF DF EF FF GF
AG BG CG DG EG FG GG
比如说现在插入A
先拿第3列来说,所有7个距离都变成了,他们分别用自己已有的距离去和 CA+A到他们的距离 做比较取最小。
但此时CA是最优的吗?A到他们是最优的吗?他们已有的是最优的吗?
不用去算,直接看第2步插完A的结论,插A的比如CG应该是CG的本身和CA+AG作比较,取最小,这第2步里的图插完A,CG还是INF呢,显然上面提到的3个都不是最优解,而CG是在之后的插B和插E两次更新的数值,这就是每次更新的思想。
但问题来了,能否把中介点,即插入点,放到3个for循环的最里层,也就是floyed可以交换for顺序么。参考博客
详细解释,还是上面那49个距离,遍历插入点for放到最里层,比如现在外层俩for遍历到AD这个点了,最里层for做遍历插入操作,直接一次性遍历插入A~G这几个点,其实就是更新
AA+AD AB+BD AC+CD AD+DD AE+ED AF+FD AG+GD
发现这tmd全是INF啊,算法里插C和F的时候AD距离做的跟新,可现在CF插了没啥用
故此,感觉是不可以换顺序的
中介插入点确定好在最外层,剩下两个for确定一个点在最里层,只要定下这个顺序,至于插入点和两个for自己内部怎么顺序插入和遍历点都无所谓了,我用上面棒棒图解博客里的代码试过了,具体原理有点太复杂,先搁置
有个粗略的想法就是:现在有个唯一的疑问比如B插入,然后C这个....,C插入是在B基础上的,有没有可能不走B,直接走C是最短路,就是说如果走B会更优,矩阵就先更新了,但再走C,就超级远,而只走C就比走B再走C近很多这种情况
举个例子:
先假设没有F点
不考虑两边之和大于第三边,以为那样的话会搞很多个点和边,不方便看了
比如想知道A到E的最短距离,那直线AE首先加入答案可能集合。
上面说过,对于中介插入点顺序没要求,那我比如先插C
首先明确一点,对于某个距离,算法是每次都取最小值,那我只要保证该距离的所有情况都出现过就OK了
插C后,AC+CE加进来,然后是B到E这个距离,除了起初的BE还有BC+CE
插B后,AB+BE加进来,然后沾亲带故的更新
假设说ABCE是最优路径的话(尼玛我ABCE路上修了穿云箭不行么,其他都是步行)
可以看到,
插C的时候,BC+CE比BE更优,B到E经过的C点记下来了
插B的时候,AB+BE比起初的AE更优,B也记录下来了
至此A经过B经过C到E是最优就解出来了(发现写着写着更加理解了,F点用不到了,画图的时候还有点不清晰,导致加了个F论述下)
注意到这里插C后,BC+CE是在插B的前面,那都说了不用考虑顺序,如果先插B再插C,AB+BE事先就更新了,插C后有虽然有BC+CE,但也用不上了啊,人家AB+BE要就更新了
那再先插B再插C看下
插B后,A到E的AB+BE进来,A到C点也就有AB+BC加进来
插C后,AC+CE进来。这里A到C的距离,肯定有之前AC和AB+BC比较,最优是AB+BC,那现在又加了个C,局面就是A到C走B,再到E走C,路线ABCE就出来了
综上,思想就是把所有可能都列出来,然后由3层for里的关键语句去取最小。上面的例子也可看出,也不存在由于最优路线上的点后插入,导致没更新成最优距离的情况
来看青蛙这个题
发现得先处理输入数据,任意俩数之间距离,回顾组合数公式,n个数中取m个,我跟个专科生一样,啥都不会了艹,跟高考似得还学数学
学半天好像学错算法了,咋感觉不是弗洛伊德算法呢(想用迪杰斯特拉发现迪杰斯特拉又忘了我艹!!)
回顾了下感觉并不是单纯迪杰斯特拉或者弗洛伊德
举例子, 比如这个图,总共三条路:
AFGHIJB这条路前面每段距离都是1,最后J到B的距离是7,即1+1+1+1+1+7(5个1,1个7)
ADB距离是2+5
ACEB距离是3+3+3
显然现成算法求最短路是2+5,但这个题答案显然是ACEB这条路
好恶心啊这个题,像是发明一种新算法的感觉,都不敢写了
如果说用迪杰斯特拉的话,每次求起点到某点的最短路也没意义啊,而且记录vis好像也没用
好像又是弗洛伊德了
遍历所有路径?200个点总共多少路径?不知道,但可以用他的思路,插入点,总共200个点,插200次,大更新,只要加入k点后,i到k和k到j中的最大值,比我目前存的i到j的最大值小,就更新,我写试试
将学过的融入血液,然后忘记所有的学过的东西,去写才能写出来,写完发现tm好像是个动归,但很奇怪的是如果一开始想动归的思路,估计反而写不出来
写了个代码WA了
1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 #include<math.h> 5 #include<iomanip> 6 using namespace std; 7 int n; 8 int stone_x[200];//stone_x[3]表示第3个石头的横坐标 9 int stone_y[200]; 10 double dir[200][200];//dir[1][199]表示第2个石头距离第200个石头距离,主要用于存储初始输入数据 11 double maxdir[200][200];//maxdir[0][199]从第1个点到第200个点中途经每段的最大距离 12 int main() 13 { 14 int t=1; 15 freopen("zhishu.txt","r",stdin); 16 while(cin>>n&&n) 17 { 18 memset(dir,0x3f,sizeof(dir)); 19 20 21 for(int i=0;i<n;i++) 22 cin>>stone_x[i]>>stone_y[i]; 23 24 //由于第二个点是目的地,所以要处理数据,跟最后一个点交换 25 int change_x; 26 int change_y; 27 change_x=stone_x[1]; 28 change_y=stone_y[1]; 29 stone_x[1]=stone_x[n-1]; 30 stone_y[1]=stone_y[n-1]; 31 stone_x[n-1]=change_x; 32 stone_y[n-1]=change_y; 33 //至此第一个点到最后一个点就是要求的 34 35 //现在开始做俩俩点之间的距离赋值 36 for(int i=0;i<n;i++) 37 for(int j=i+1;j<n;j++){ 38 dir[i][j]=sqrt(pow(stone_x[i]-stone_x[j],2)+pow(stone_y[i]-stone_y[j],2)); 39 dir[j][i]=dir[i][j]; 40 } 41 for(int i=0;i<n;i++) 42 dir[i][i]=0;//自身到自身为0 43 // 至此数据处理结束 44 45 // 一开始想迪杰斯特拉,做了交换,现在改用弗洛伊德交换其实就不用了 46 // memset(maxdir,0x3f,sizeof(maxdir)); 47 memset(maxdir,0x42,sizeof(maxdir)); 48 // cout<<"#"<<maxdir[0][0]<<endl; 49 for(int k=0;k<n;k++){ 50 for(int i=0;i<n;i++) 51 for(int j=0;j<n;j++){ 52 //有时候真觉得看别人代码是种累赘和反效果,很多坑自己踩了才能清晰的了解算法逻辑思想,自己写过才能形成自己的代码风格而不是留着别人的代码印记写个四不像,调bug又不敢调 这一步就想好久要怎么写 53 if(maxdir[i][j] > max(dir[i][k],dir[k][j])){ 54 maxdir[i][j] = max(dir[i][k],dir[k][j]);//让每段的最大值最小,比如2+4和5,最大值分别为4和5,我要取2+4这条路 55 // cout<<"%"<<i<<" "<<j<<" "<<max(dir[i][k],dir[k][j])<<" "<<maxdir[i][j]<<endl; 56 } 57 } 58 } 59 cout<<"Scenario #"<<t++<<endl; 60 cout<<"Frog Distance = "<<fixed << setprecision(3)<<maxdir[0][n-1]<<endl; 61 cout<<endl; 62 } 63 }
写了个对拍程序生成测试数据
1 //#include<bits/stdc++.h> 2 #include<stdio.h> 3 #include<iostream> 4 #include<time.h> 5 using namespace std; 6 char map[2]={'.','#'}; 7 int main() { 8 srand(time(0) + (unsigned long long)(new char)); 9 // int T = rand() % 200; 10 int T = rand() % 50; 11 cout<<T<<endl; 12 while(T--){ 13 int M = rand() % 1000; 14 int N = rand() % 1000; 15 16 cout<<M<<" "<<N<<endl; 17 // for(int i=0;i<M;i++){ 18 // for(int j=0;j<N;j++){ 19 // int q = rand() % 2; 20 // cout<<map[q]; 21 // } 22 // cout<<endl; 23 // } 24 } 25 }
反例:
4 3 8 7 3 7 4 3 5
发现
1、没对自身到自身的maxder初始化,如果1到1这个点,应该是0,加入某个距离他是5的点后,会由于初始值为0x42,做出0x42和max(5,5)作比较,从1到1距离更新为5,但应该是0
2、没对两点之间的距离初始化
3、比较的时候,应该用更新后的maxdir去比,而不要再用dir了
更改后,直接AC
AC代码 (两个平台均可AC)
1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 #include<math.h> 5 #include<iomanip> 6 using namespace std; 7 int n; 8 int stone_x[200];//stone_x[3]表示第3个石头的横坐标 9 int stone_y[200]; 10 double dir[200][200];//dir[1][199]表示第2个石头距离第200个石头距离,主要用于存储初始输入数据 11 double maxdir[200][200];//maxdir[0][199]从第1个点到第200个点中途经每段的最大距离 12 int main() 13 { 14 int t=1; 15 // freopen("zhishu.txt","r",stdin); 16 while(cin>>n&&n) 17 { 18 memset(dir,0x3f,sizeof(dir)); 19 20 memset(maxdir,0x42,sizeof(maxdir)); 21 22 for(int i=0;i<n;i++) 23 cin>>stone_x[i]>>stone_y[i]; 24 25 //由于第二个点是目的地,所以要处理数据,跟最后一个点交换 26 int change_x; 27 int change_y; 28 change_x=stone_x[1]; 29 change_y=stone_y[1]; 30 stone_x[1]=stone_x[n-1]; 31 stone_y[1]=stone_y[n-1]; 32 stone_x[n-1]=change_x; 33 stone_y[n-1]=change_y; 34 //至此第一个点到最后一个点就是要求的 35 36 //现在开始做俩俩点之间的距离赋值 37 for(int i=0;i<n;i++) 38 for(int j=i+1;j<n;j++){ 39 dir[i][j]=sqrt(pow(stone_x[i]-stone_x[j],2)+pow(stone_y[i]-stone_y[j],2)); 40 dir[j][i]=dir[i][j]; 41 maxdir[i][j]=dir[i][j]; 42 maxdir[j][i]=dir[i][j]; 43 } 44 45 for(int i=0;i<n;i++){ 46 dir[i][i]=0;//自身到自身为0 47 maxdir[i][i]=0; 48 } 49 50 51 for(int k=0;k<n;k++){ 52 for(int i=0;i<n;i++) 53 for(int j=0;j<n;j++){ 54 //有时候真觉得看别人代码是种累赘和反效果,很多坑自己踩了才能清晰的了解算法逻辑思想,自己写过才能形成自己的代码风格而不是留着别人的代码印记写个四不像,调bug又不敢调 这一步就想好久要怎么写 55 if(maxdir[i][j] > max(maxdir[i][k],maxdir[k][j])){ 56 maxdir[i][j] = max(maxdir[i][k],maxdir[k][j]);//让每段的最大值最小,比如2+4和5,最大值分别为4和5,我要取2+4这条路 57 // cout<<i<<" "<<j<<" "<<k<<" "<<maxdir[i][j]<<endl; 58 } 59 } 60 } 61 cout<<"Scenario #"<<t++<<endl; 62 cout<<"Frog Distance = "<<fixed << setprecision(3)<<maxdir[0][n-1]<<endl; 63 cout<<endl; 64 } 65 }
其实dir的memset,0x3f有问题,只是不需要赋值,真有用的话这语句赋的值问题
昨晚想了好久这题都耽误我撸管子了
想看看别人都咋写的代码,结果发现,为啥我看不懂~~~~(>_<)~~~~
这ACM大佬博客写的什么JB玩意,再看下去我要疯掉了
还有这个参考博客。懒得看了直接下一题
中途遇到的大坑:
###:
1 也就我这种笨人这么研究,二哥那种聪明的直接1s想通 2 我没空搜B站 3 想透彻理解B站视频啥的查一查估计更加清晰直观 4 我只看博客文章没空看视频,觉得看视频low 5 但其实看再多也没用,拉屎走路撸管子吃饭没事多想想。自己想通才是最重要的 6 7 润书房丰满侧颜极美少妇 8 一个小麦肤色维族认真学习的女孩仿佛从画中走出总带个白色耳机,好美 9 无法专心 10 11 理解的了无忌太极拳,忘记招数才是最好的,找那个当时挥拳破敌的感觉 12 学习新算法也是,回忆思路自己憋代码,想当时算法的思路要点,可以先看,但看完自己写的时候一眼都别再看硬憋,且尽量别回忆别人的代码(只想思路,我这一步要干啥要用到啥),不然容易陷入代码写的四不像缺东少西自己又找不到错误的境地
###:棒棒图解博客里的数据
1 0 12 1000 1000 1000 16 14 2 12 0 10 1000 1000 7 1000 3 1000 10 0 3 5 6 1000 4 1000 1000 3 0 4 1000 1000 5 1000 1000 5 4 0 2 8 6 16 7 6 1000 2 0 9 7 14 1000 1000 1000 8 9 0
###:
double类型的数组初始化最大值的时候,memset 0x3f有问题,double的memset赋值,真的是全网都是互相抄,都他妈在那放没用的屁,没找到为啥用1,通过memset方式赋值给double类型的数组,结果都是0,而0x3f是个很小的数
00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001
这你妈是0?
###:拿来直接用的:double类型,用cout输出,控制输出的小数点位数的函数
###:发现很多算法也好定义页也好,不说人话是因为要用好多符号和文字描述一个通式,这个通式适用于范围很大的情况,所以能一句话清晰明了的解释清楚的东西,好多傻逼定义用十句话先描述下大范围的情况,再说要点,这样新手根本看不下去,所以尽量别看定义和算法科普式的百科介绍