POJ3268-Silver Cow Party
继续刷邝斌带你飞专题
垃圾POJ不出意外的话依旧是挂
洛谷YYDS 要不是看了眼 标签USACO2007 差点就错过这个题了,第一次遇到题目名字不一样的
那个可用平台也是YYDS但是这题没有
10^3个点,10^5条路,每条路最大10^2,弗洛伊德的每次插点的方法显然10^9超时
有了上个题的基础(我发现邝斌的专题真牛逼,阶梯型太好了,之前搜索的记录状态也是),感觉挺容易的
题目的样例图解看都没看,怕先看图后形成定势思维,自己不会画图了,影响我找bug
简单划了划了,就是搞两遍,先从要去的点(x)出发,跑一遍迪杰斯特拉,把它到所有点的最短路径存下来,这时候题意中的回程就有了,再每一个点跑一遍迪杰斯特拉找去程,妈的直接干超时了艹,一次迪杰斯特拉是n^2,这直接(n^2+n^3)
连迪杰斯特拉都超时了,果断看题解
简单扫一眼百度结果列表,居然说是迪杰斯特拉和有向图处理,麻痹的,不能再看了,自己想硬憋吧
再次科普下都有哪些最短路算法,发现有个邻接表+优先队列感觉有用(但时间复杂度他写的M*logN纯属放屁!和边M都没关系),讲的也相当之tmd浅显(涉及到了运算符重载,全网找不到人写的好博客,都是跟狗写的一样乱七八糟,姑且先放个能看的)
记得上一个题看别人解法有出现队列的,我还纳闷咋就能直接出队列不用比较的(我看到这些命名很奇怪的各种高科技就很烦,比如上一篇文章出现的 priority_queue<PII> pq; ),其实就是优先队列,展开学下(其实都学过只是学的时候静不下心学的不踏实,全忘了)
查了下什么是优先队列,不错的科普文章,但只是讲解了优先队列这个数据结构的原理的底层实现(即堆排序),但只是指了个路,依旧很朦胧
进一步发现了一个好到让人高潮的良心极品博客,搭配这个介绍堆排序的图看,更加具象了优先队列这个数据结构的底层原理思想,思路更清晰了(但代码不知道咋写),回顾完堆排序,但堆排序即优先队列,跟迪杰斯特拉有啥关系?
最后找到优先队列和迪杰斯特拉结合到一起的好博客(重点是代码解释的相当清楚)
最终理解:迪杰斯特拉先遍历一遍,然后对于每次遍历,都再遍历一遍找出最大或者最小值,然后对于每个找到的极值,都再遍历一遍做沾亲带故的松弛,即 n * (n+n),这就是n^2,其实换个角度想就是排好序每次找极值并松弛
优先队列法的迪杰斯特拉我起初始终没想,松弛每次都是n,优先队列内部堆排序是nlogn,那不应该是n*n*logn吗?查到这篇关于堆排序复杂度的博客,但这人的博客最后那个删除节点的疑问不对啊,好像不是2上浮,具体是啥等学排序后再研究吧。堆排序建立堆的时间复杂度是N的精品推导
走路的时候我想通了,堆排序先建堆再做n次调整(每次是logn),看下面知识点###8,但迪杰斯特拉优先队列法,不用调整,我只需要取堆顶最大即可,
优先队列每次松弛其实就是往队列里加入点,起初是只有一个点,松弛找到最坏情况n个点全加入队列,相当于优先队列里自己内部建堆,那就是n,取数复杂度是1,这块感觉没有人能说清,不是简简单单的堆排序是nlogn我迪杰斯特拉优先队列就也是nlogn那么简单,tmd一群傻狗,但我也想不通了,划了划了放个草纸先搁置,硬记吧,以后遇到大佬讨论懂了再更新
我觉得只能先强行解释下安慰自己,总共n个点,要弹出n次,每弹出点后,也就是堆顶那个点,对剩下的要把最后的调整到对头然后下沉,也就是要进行logn的调整找新堆顶,到这是nlogn,
然后对于找出的点,松弛一遍就是n,但不能无序啊,就算建堆是n
一相乘就是n*n*logn了啊~~~~(>_<)~~~~
就算单看那个好博客里的代码
while(!qq.empty()){
for(int i = 2 ; i <= V ; i++){
if(D[qq.top().num][i] != -1 && dis[i] > dis[qq.top().num]+D[qq.top().num][i]){ // 松弛操作。 dis[i] = dis[qq.top().num] + D[qq.top().num][i]; nod.num = i; nod.val = dis[i]; qq.push(nod); // 把点加入到 } } qq.pop();
}
n次松弛,非空弹出肯定是n次因为n个点嘛,那就是n^2啊
这个复杂度先搁置了,权且当作是nlogn了
做升序排列用大顶堆,我又不会了,看了一堆,就这博客写的很清楚
发现不写重载 bool operator < (Node a ,Node b){ if(a.val == b.val) return a.num > b.num; return a.val > b.val; } 会报错,报错还很奇怪(仿佛是优先队列的源码,无奈查了下重载,const说是对象,我好烦C++这些不认识的东西,查了个博客,姑且先用着(原理搁置),上面那个重载等号很简单,涉及到大小的重载才是我不会的
回到这个题,2起点跑一遍得出2分别到134的最小值,当作回程,1跑一遍完整的迪杰斯特拉然后取1到2的结论当作1的去程,34同理,所以n个点跑n遍迪杰斯特拉
第一次代码WA

1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 #include<queue> 5 using namespace std; 6 int n,m,x; 7 int dir[1001][1001]; 8 struct Node{ 9 int num;//编号,给自己用的用于做松弛,指代某个点。起初我觉得用不上这个num 10 int weight;//起点到这点的总路线权重,给优先队列内部,函数内部他自己用的,做个比较 11 }; 12 //int vis[1001]; 13 int ans[1001][1001];//正常迪杰斯特拉是记录终点,但这里要跑n遍,不同的起点都要跑 14 priority_queue<Node>q; 15 bool operator<(Node a ,Node b){//奇怪这个<不会在正常使用的时候也用上它吗 16 // if(a.weight==b.weight) return a.num < b.num;//麻痹参考博客里写的太有误导性,气死就是大的在前面,权重一样就不动 17 return a.weight>b.weight;//意思就是每次弹出队列的是最小weight,默认上大下小的大顶堆,但左右孩子大小不确定,但根的俩孩子中一定有除了根外最大的数,所以根跟最后一个叶子节点交换然后之前根的孩子上来,又是最大的,再跟倒数第二个叶子交换,最终数组是从小到大排序的 18 }//回忆手把手教L雪T复试刷题,调bug,说注释可爱,他说我让他知道为什么错,如今的我... 19 //赵健:直接用之前的改吧改吧,复制粘贴我就回来了 20 Node firstnode; 21 Node thisnode; 22 Node nextnode; 23 int main() 24 { 25 while(cin>>n>>m>>x){ 26 memset(dir,0x3f,sizeof(dir)); 27 memset(ans,0x3f,sizeof(ans)); 28 int a,b;//工具人 29 for(int i=0;i<m;i++) 30 cin>>a>>b>>dir[a][b]; 31 for(int i=1;i<=n;i++){ 32 dir[i][i]=0;//自身到自身是0,其实没啥用,便于调试代码,下面松弛到自身的时候,用到,这里:dir[thisnode.num][j] 33 ans[i][i]=0; 34 } 35 for(int i=1;i<=n;i++){//每个点都跑一遍迪杰斯特拉 36 // memset(vis,0,sizeof(vis));//理论上也用二维的,ans作为答案没法改变,比如2到4的最小和3到4的最小不能都用ans[4],但vis对于这次2为起点到134全0,下次3为起点到124vis全0是可以的,vis相当于工具人变量,每次memset一下就OJBK 37 // 但发现不用vis,朴素版本的是把比完的vis赋1,下次不用参与比较了,这里比完的沾亲带故送松弛后会pop弹出了,故不用vis 38 firstnode.num=1; 39 firstnode.weight=0;//唯一作用是下面遍历给起点周围有路的点,赋值,其实就是自身到自身的权重 40 // ans[i][i]=0;//唯一作用是松弛的时候自身到自身是0,那个比较0不可能大于任何数,也就是不必松弛,其实下面比较前加一个if(i!=j)也行,for j 那里 41 // vis[i]=1;//访问标记 42 q.push(firstnode); 43 while(!q.empty()){ 44 thisnode=q.top();//队列用的是front 45 for(int j=1;j<=n;j++){ 46 if(ans[i][j]>thisnode.weight+dir[thisnode.num][j]){ 47 ans[i][j]=thisnode.weight+dir[thisnode.num][j]; 48 nextnode.num=j; 49 nextnode.weight=ans[i][j]; 50 q.push(nextnode); 51 } 52 } 53 q.pop();//滚犊子,下次别再进来参与比较了。由你的下一个点参与 54 } 55 } 56 int maxnum=0; 57 for(int i=1;i<=n;i++){ 58 if(i==x) 59 continue; 60 if(ans[i][x]+ans[x][i]>maxnum) 61 maxnum=ans[i][x]+ans[x][i]; 62 } 63 64 cout<<maxnum<<endl; 65 } 66 }
感觉迪杰斯特拉算法最难的就是memset究竟设多少,自身到自身设多少,没有路设多少这些问题
但这个题显示1.31s,只有一个数据对,10分,之前说感觉这种能看到得分的测评不好,会知道自己改的方向对不对,影响提升调试能力,可是poj还没好,但也看出来了,测评是先判断数据对不对,如果数据都对,则TLE,这题时限1s。
发现是firstnode.num初始值赋错了,更改后,直接AC
AC代码 —— 堆优化优先队列法的迪杰斯特拉,用时1.65s
1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 #include<queue> 5 using namespace std; 6 int n,m,x; 7 int dir[1001][1001]; 8 struct Node{ 9 int num; 10 int weight; 11 }; 12 int ans[1001][1001]; 13 priority_queue<Node>q; 14 bool operator<(Node a ,Node b){ 15 return a.weight>b.weight; 16 } 17 Node firstnode; 18 Node thisnode; 19 Node nextnode; 20 int main() 21 { 22 while(cin>>n>>m>>x){ 23 while(!q.empty()) 24 q.pop(); 25 memset(dir,0x3f,sizeof(dir)); 26 memset(ans,0x3f,sizeof(ans)); 27 int a,b;//工具人 28 for(int i=0;i<m;i++) 29 cin>>a>>b>>dir[a][b]; 30 for(int i=1;i<=n;i++){ 31 dir[i][i]=0; 32 ans[i][i]=0; 33 } 34 for(int i=1;i<=n;i++){ 35 firstnode.num=i; 36 firstnode.weight=0; 37 q.push(firstnode); 38 while(!q.empty()){ 39 thisnode=q.top(); 40 // cout<<"#"<<thisnode.weight<<endl; 41 for(int j=1;j<=n;j++){ 42 if(ans[i][j]>thisnode.weight+dir[thisnode.num][j]){ 43 ans[i][j]=thisnode.weight+dir[thisnode.num][j]; 44 nextnode.num=j; 45 nextnode.weight=ans[i][j]; 46 // cout<<"@"<<nextnode.weight<<endl; 47 q.push(nextnode); 48 } 49 } 50 q.pop(); 51 } 52 } 53 int maxnum=0; 54 for(int i=1;i<=n;i++){ 55 if(i==x) 56 continue; 57 if(ans[i][x]+ans[x][i]>maxnum) 58 maxnum=ans[i][x]+ans[x][i]; 59 } 60 cout<<maxnum<<endl; 61 } 62 }
23、24行的清空没必要,每次必是空的
另外说个事,这题AC时间1.65s???题目写的时间限制1s
邝斌飞vjudge是2s,这个垃圾平台完全套壳POJ,POJ崩的时候他提交不了(话说vjudge也是这样的),这个垃圾平台显示也是2s,所以估计poj也是2s(poj崩溃好几天了,打不开官网),估计洛谷写错了
我一开始还在想,为啥初始值赋错了会1s多,且返回的是WA,发现1s多本来就不超时,实际限制是2s
但我又想个事,为啥会1s多??
输入m条边,复杂度:m
自身到自身距离初始化:复杂度n
n个迪杰斯特拉:n*nlogn
遍历找max:n
这也就是m+n+n*nlogn+n,这也就是10^6啊,就1s多啦???
先搁置
看看其他人咋写的,
离奇,这人用弗洛伊德居然不TLE,估计时限2s可以10^9
怎么总是写完弗洛伊德就忘迪杰斯特拉,写完迪杰斯特拉就忘弗洛伊德,但每次都会比之前对算法思想的理解更加深刻,不单单是回顾起之前的那个理解程度
好奇于是写了个弗洛伊德算法的代码,居然没AC,(果然错了,发现只要写就一定有提升)

1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 #include<queue> 5 using namespace std; 6 int n,m,x; 7 int dir[1001][1001]; 8 int ans[1001][1001]; 9 int main() 10 { 11 // freopen("zhishu.txt","r",stdin); 12 while(cin>>n>>m>>x){ 13 memset(dir,0x3f,sizeof(dir)); 14 memset(ans,0x3f,sizeof(ans)); 15 int a,b;//工具人 16 for(int i=0;i<m;i++){ 17 cin>>a>>b>>dir[a][b]; 18 ans[a][b]=dir[a][b]; 19 } 20 for(int i=1;i<=n;i++){ 21 dir[i][i]=0; 22 ans[i][i]=0; 23 } 24 for(int k=1;k<=n;k++) 25 for(int i=1;i<=n;i++) 26 for(int j=1;j<=n;j++){ 27 if(ans[i][j]>ans[i][k]+dir[k][j]){ 28 ans[i][j]=ans[i][k]+dir[k][j]; 29 // cout<<k<<" "<<i<<" "<<j<<" "<<ans[i][j]<<endl; 30 } 31 } 32 int maxnum=0; 33 for(int i=1;i<=n;i++){ 34 if(i==x) 35 continue; 36 if(ans[i][x]+ans[x][i]>maxnum) 37 maxnum=ans[i][x]+ans[x][i]; 38 } 39 cout<<maxnum<<endl; 40 } 41 }
写了个对拍,发现小坑点取余操作不可以对0取余,对拍程序

1 #include<stdio.h> 2 #include<iostream> 3 #include<time.h> 4 using namespace std; 5 6 int main(){ 7 srand(time(0) + (unsigned long long)(new char)); 8 int n = rand()%5+1; 9 int m = rand()%7+1; 10 int x=rand()%n; 11 cout<<n<<" "<<m<<" "<<x<<endl; 12 for(int i=0;i<m;i++){ 13 int a=rand()%n+1; 14 int b=rand()%n+1; 15 int c=rand()%100+1; 16 cout<<a<<" "<<b<<" "<<c<<endl; 17 } 18 }
对拍数据(3到2没距离,但没事,针对我的代码只是简单验证下2到3就找出代码错误点了)
5 6 3 1 5 96 4 3 17 2 1 4 5 1 8 3 5 6 5 4 44
适配图
发现代码写错了,更正后AC了(之前写错了,把下面AC代码的27行的ans写成了dir(发现dir是迪杰斯特拉里沾亲带故的意思),居然还能对前5个数据)
AC代码(弗洛伊德版本)—— 用时反而更少???1.25s
1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 #include<queue> 5 using namespace std; 6 int n,m,x; 7 int dir[1001][1001]; 8 int ans[1001][1001]; 9 int main() 10 { 11 // freopen("zhishu.txt","r",stdin); 12 while(cin>>n>>m>>x){ 13 memset(dir,0x3f,sizeof(dir)); 14 memset(ans,0x3f,sizeof(ans)); 15 int a,b;//工具人 16 for(int i=0;i<m;i++){ 17 cin>>a>>b>>dir[a][b]; 18 ans[a][b]=dir[a][b]; 19 } 20 for(int i=1;i<=n;i++){ 21 dir[i][i]=0; 22 ans[i][i]=0; 23 } 24 for(int k=1;k<=n;k++) 25 for(int i=1;i<=n;i++) 26 for(int j=1;j<=n;j++){ 27 if(ans[i][j]>ans[i][k]+ans[k][j]){ 28 ans[i][j]=ans[i][k]+ans[k][j]; 29 // cout<<k<<" "<<i<<" "<<j<<" "<<ans[i][j]<<endl; 30 } 31 } 32 int maxnum=0; 33 for(int i=1;i<=n;i++){ 34 if(i==x) 35 continue; 36 if(ans[i][x]+ans[x][i]>maxnum) 37 maxnum=ans[i][x]+ans[x][i]; 38 } 39 cout<<maxnum<<endl; 40 } 41 }
又对弗洛伊德有了更深的理解,上面代码的28行,全都是ans,ans事先都赋值好为初始路径长度
再看其他人代码,
发现自己好像学多了,不过无妨,因为之前没思路扫一眼题解,说是迪杰斯特拉和有向图处理,我以为迪杰斯特拉的邻接表存图,然后误打误撞学了堆优化优先队列,发现并不是邻接表,然后AC了,邻接表始终没学,也没有题目必须用这个,感觉学了写完也不知道是不是对的,先搁置,看洛谷讨论区发现有人跟之前最开始百度结果列表反向存图是一样的思路
理解叉劈了才学了优先队列(时候发现这个是最正统的接法),讨论区有人说SPFA,但这算法好像很容易被出题人卡?不懂啥意思,先搁置
反向存图,极好的思维方法!!我一直以为要跑n遍迪杰斯特拉,用了高科技优先队列,跑n遍AC掉了,结果反向存图只需要2遍就行,虽说多学算法掌握高科技很有必要,但如果当初能想到这个方法(简直就是降维打击),真不一定去看优先队列,好讨厌学新东西
再写下反向建地图仅仅跑2遍朴素迪杰斯特拉写法:
不需要你其他点到我x,只需要我x到你的距离就行,比如
1到2是6,
3到2是7
4到2是8
x是2
去程就是678仨距离,有几个点就要跑几遍为了找出某点到x这个最短路,但反过来存图就可以无论有几个点都只需要跑一遍,当作x也就是2出发
2到1当作是6,2到3当作7,2到4当作8,至于2到1本身是多少不用管,不需要(上面的堆优化版本的迪杰斯特拉跑的n次也都是起点为开始,到目的地,并不需要某点到起点的距离)
WA了,

1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 #include<queue> 5 using namespace std; 6 int n,m,x; 7 int dir[1001][1001]; 8 int ans_return[1001]; 9 int ans_go[1001]; 10 int a,b; 11 int vis[1001]; 12 int main() 13 { 14 while(cin>>n>>m>>x){ 15 memset(dir,0x3f,sizeof(dir)); 16 memset(ans_return,0x3f,sizeof(ans_return)); 17 memset(vis,0,sizeof(vis)); 18 for(int i=0;i<m;i++) 19 cin>>a>>b>>dir[a][b]; 20 21 for(int i=1;i<=n;i++) 22 dir[i][i]=0; 23 ans_return[x]=0; 24 int p; 25 int mixnum; 26 for(int i=0;i<n;i++){//回程,x出发,到各个点 27 mixnum=0x3f3f3f3f; 28 for(int j=1;j<=n;j++){ 29 if(ans_return[j]<mixnum&&vis[j]==0){ 30 p=j; 31 mixnum=ans_return[j]; 32 } 33 } 34 vis[p]=1; 35 for(int k=1;k<=n;k++){ 36 if(ans_return[k]>ans_return[p]+dir[p][k])//查看和弗洛伊德的区别 37 ans_return[k]=ans_return[p]+dir[p][k]; 38 } 39 } 40 41 // 重新反向建图,作为去程,即某到x 42 memset(ans_go,0x3f,sizeof(ans_go)); 43 memset(vis,0,sizeof(vis)); 44 45 for(int i=1;i<=n;i++)//反向建图 46 if(dir[i][x]!=0x3f3f3f3f)//你"x到某"有路,就把这条路的距离给我"某到x"身上 47 dir[x][i]=dir[i][x];//i==x的情况不用考虑,0给0而已,x到某的距离用不到作废 48 ans_go[x]=0; 49 for(int i=0;i<n;i++){//去程,x出发,到各个点 50 mixnum=0x3f3f3f3f; 51 for(int j=1;j<=n;j++){ 52 if(ans_go[j]<mixnum&&vis[j]==0){ 53 p=j; 54 mixnum=ans_go[j]; 55 } 56 } 57 vis[p]=1; 58 for(int k=1;k<=n;k++){ 59 if(ans_go[k]>ans_go[p]+dir[p][k])//查看和弗洛伊德的区别 60 ans_go[k]=ans_go[p]+dir[p][k]; 61 } 62 } 63 64 65 int maxnum=0;//比较 66 for(int i=1;i<=n;i++){ 67 if(maxnum<ans_go[i]+ans_return[i]) 68 maxnum=ans_go[i]+ans_return[i]; 69 70 } 71 cout<<maxnum<<endl; 72 } 73 }
用上面的数据(答案应该是2点最远,161+1061109567=1061109728)
5 6 3 1 5 96 4 3 17 2 1 4 5 1 8 3 5 6 5 4 44
找了好久发现是去程ans_go的代码写错了,结合上面适配图,测试发现
1、以为只用把“某到x”赋给“x到某”就行,其他不用管,实际发现其他所有点也应该倒着赋值一下
2、用不上的应该是所有之前的值,比如a到b,赋给b到a,a到b就用不上了,并且应该赋值为无穷大
修改后,直接AC
AC代码 ——(最朴素的迪杰斯特拉,只跑2遍,76ms)
1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 #include<queue> 5 using namespace std; 6 int n,m,x; 7 int dir[1001][1001]; 8 int dir_go[1001][1001]; 9 int ans_return[1001]; 10 int ans_go[1001]; 11 int a,b; 12 int vis[1001]; 13 int main() 14 { 15 while(cin>>n>>m>>x){ 16 memset(dir_go,0x3f,sizeof(dir_go)); 17 memset(dir,0x3f,sizeof(dir)); 18 memset(ans_return,0x3f,sizeof(ans_return)); 19 memset(vis,0,sizeof(vis)); 20 for(int i=0;i<m;i++){ 21 cin>>a>>b>>dir[a][b]; 22 dir_go[b][a]=dir[a][b]; 23 } 24 for(int i=1;i<=n;i++){ 25 dir[i][i]=0; 26 dir_go[i][i]=0; 27 } 28 ans_return[x]=0; 29 int p; 30 int mixnum; 31 for(int i=0;i<n;i++){ 32 mixnum=0x3f3f3f3f; 33 for(int j=1;j<=n;j++){ 34 if(ans_return[j]<mixnum&&vis[j]==0){ 35 p=j; 36 mixnum=ans_return[j]; 37 } 38 } 39 vis[p]=1; 40 for(int k=1;k<=n;k++){ 41 if(ans_return[k]>ans_return[p]+dir[p][k]) 42 ans_return[k]=ans_return[p]+dir[p][k]; 43 } 44 } 45 46 memset(ans_go,0x3f,sizeof(ans_go)); 47 memset(vis,0,sizeof(vis)); 48 49 ans_go[x]=0; 50 for(int i=0;i<n;i++){ 51 mixnum=0x3f3f3f3f; 52 for(int j=1;j<=n;j++){ 53 if(ans_go[j]<mixnum&&vis[j]==0){ 54 p=j; 55 mixnum=ans_go[j]; 56 } 57 } 58 vis[p]=1; 59 for(int k=1;k<=n;k++){ 60 if(ans_go[k]>ans_go[p]+dir_go[p][k]){ 61 // cout<<"@ "<<p<<" "<<k<<" "<<ans_go[k]<<endl; 62 ans_go[k]=ans_go[p]+dir_go[p][k]; 63 // cout<<"# "<<p<<" "<<k<<" "<<ans_go[k]<<endl; 64 } 65 } 66 } 67 68 int maxnum=0; 69 for(int i=1;i<=n;i++) 70 if(maxnum<ans_go[i]+ans_return[i]) 71 maxnum=ans_go[i]+ans_return[i]; 72 cout<<maxnum<<endl; 73 } 74 }
又对迪杰斯特拉有了更深的理解,上面代码的62行,是dir距离,dir表示沾亲带故的更新
综上:
堆优化迪杰斯特拉: n^2logn 1.65s
弗洛伊德: n^3 1.25s
2遍朴素的迪杰斯特拉: n^2 76ms
话说时限2s那n^2复杂度的迪杰斯特拉,跑n遍也可以AC
懒得再写了
妈的强迫症犯了,再去写下吧

真的值得么,傻逼废物垃圾废人,一无是处穷途末路
第一次提交9WA+1TLE,右上角时间2.3s,分数0,
再次提交1.96s,第二个点TLE,时限不是2s吗??而且同样是n^3前面那个弗洛伊德是1.25sAC,这个居然不行,不管了(但这个思路的迪杰斯特拉有加深了这个算法的理解,代码里有详尽注释)
1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 #include<queue> 5 using namespace std; 6 int n,m,x; 7 int dir[1001][1001]; 8 int ans[1001][1001]; 9 int a,b; 10 int vis[1001]; 11 int p; 12 int mixnum; 13 int main() 14 { 15 while(cin>>n>>m>>x){ 16 memset(dir,0x3f,sizeof(dir)); 17 for(int i=0;i<m;i++) 18 cin>>a>>b>>dir[a][b]; 19 for(int i=1;i<=n;i++) 20 dir[i][i]=0;//给下面比较中的dir[p][k]用的 21 memset(ans,0x3f,sizeof(ans)); 22 for(int v=1;v<=n;v++){//循环n次,即1~n每个点都当一回起点 23 memset(vis,0,sizeof(vis)); 24 ans[v][v]=0;//这么写有个问题就是,下次换其他点进来,到底谁是0,优先队列每次新num都重新编号0 25 if(v!=1) 26 ans[v-1][v-1]=0x3f3f3f3f;//解决上面的问题 27 for(int i=0;i<n;i++){//工具人,这个i变量用不到,只是作为循环n次使得n个点都加进去,毕竟一次加一个然后松弛所有沾亲带故的嘛,那循环n次就可以把所有点多加进去 28 mixnum=0x3f3f3f3f; 29 for(int j=1;j<=n;j++){ 30 if(ans[v][j]<mixnum&&vis[j]==0){ 31 p=j; 32 mixnum=ans[v][j]; 33 } 34 } 35 vis[p]=1; 36 for(int k=1;k<=n;k++){ 37 if(ans[v][k]>ans[v][p]+dir[p][k])//其实每次ans[v][v]为0,并让其他点自身到自身距离为无穷大的好处是 38 ans[v][k]=ans[v][p]+dir[p][k];//顶点加进来,自身为0,因为他的自身到自身是最小,那第一次松弛就相当于给所有顶点到跟顶点有距离的那些点赋值,即此时p==v,当松弛到自己顶顶点的时候0也不可能大于任何数,而误被松弛 39 // 之后其他点加进来,不会再存在什么ans0的问题了,顶点已经踢出去不会再成为p了,即v不会再等于p 40 // 那其他点的自身到自身为无穷大为啥?一次只能一个顶点,不然多个顶点一起会破坏松弛过的数据 41 //但其实用不到除了顶点外的自身到自身这个距离哪怕赋值12345都行,只要比0大就行 42 } 43 } 44 } 45 int maxnum=0; 46 for(int i=1;i<=n;i++){ 47 if(i==x) 48 continue;//自身到自身是无穷大 49 if(maxnum<ans[i][x]+ans[x][i]) 50 maxnum=ans[i][x]+ans[x][i]; 51 } 52 cout<<maxnum<<endl; 53 } 54 }
至此,此题OVER
我这猪脑子,感觉写了一堆,又啥都忘掉了~~~~(>_<)~~~~
——————————————————————————————————————————
去刷下一个题的时候发现POJ好了,把这个题提交一下,发现RE了???关于POJ返回RE可能问题的参考博客
惊了!!!!!POJ是有问题吗???洛谷AC的代码POJ不是RE就是WA
按理说POJ数据最弱,然后是hdu,数据最强的是可用平台和洛谷啊
参考博客说poj的问题,今天poj刚能修复,估计有啥没部署好吧,status也是一堆RE的
这傻逼眼睛有问题,配色给我看瞎了,但我把函数内ab和maxnum变量放到全局,居然从RE变为WA了,dir变成scanf又TLE了
发现洛谷别人的题解交到POJ有能AC的,那应该不是POJ的问题了,再把前几道题交下,发现上一道题Heavy Transportation是TLE,怎么搜索专题POJ最水,这个最短路专题POJ反而各种卡呢!!
针对上一个题,操他妈的!!cin会超时(poj讨论区仿佛上世纪的东西,谁回复的谁我一脸懵逼),上一个题AC代码已经更新,前面青蛙和奶牛回家都没问题
发现题解也全都是scanf,垃圾POJ数据弱的一批,就会在这种没意义的地方卡你,之前还有道题是油田oil吧,输入末尾多个空格导致字符串输入出现问题也是加个特判断才好
先留个证据: (md我博客里骂POJ傻逼被他们发现了?给我封了导致所有题都不过了?)
堆优化优先队列法的迪杰斯特拉,用时1.65s RE
弗洛伊德版本)—— 用时反而更少???1.25s RE
最朴素的迪杰斯特拉,只跑2遍,76ms)WA
n遍暴力朴素迪杰斯特拉(洛谷90分) WA
怎么改都是TLE,WA,RE我的天,找不到哪里错了,关键洛谷还能AC
绝望了

1 今天mm问工作+新疆下雨+RE找不到错误,逆境之下,反而坚定自己的信念,不再演戏,感觉这些年活到了狗身上,开始从未有过的专注,很不错
发现这人代码跟我差不多,下定决心学他代码里的东西,然后逐步读懂,再把我代码逐步加到里面看到底哪错了
pair和make_pair函数,百度搜 typedef pair<int, int> PII 出来的
greater()函数。以为是我没写greater,发现咋加都不对,放弃了。搜的 priority_queue<P,vector<P>,greater<P> >que;
push_back尾部加值
chrome历史记录怎么有些记录没有呢
之前搜这些写的特别复杂,导致没学的欲望,现在搜只想会用,还挺容易的,但就上午搜的时候都是浅显易懂的好文章,现在写博客复现的时候又查的都是一坨屎了
没发现问题实在绝望,想找q神,WYH问问,算了社交累了
翻了翻洛谷题解发现可以看懂

1 之前各种有偿pr,回想真的傻逼,q神都不看别人代码,但洛谷讨论区互相都可以看懂,这群中学生真牛逼
涉及的快读,先搁置
发现他是把比较写在了结构体里,
struct node { ll x,y; bool operator < (const node &a) const //把大根堆重载成小根堆。 { return y>a.y; } };
我误以为POJ老久评测系统,我的写法有问题,用他的试了下发现也没法AC(这些奇奇怪怪的高科技东西看的好头疼)
这里有个事,我一直以为建立的是默认的大根堆,没管直接用上面那个代码解释的相当清楚的好博客里的重载了,但现在一想不对啊,那个重载里为啥是重载的小于符号,然后是 return a.val > b.val ,这不就成了大的数在前面了吗,但我想要是每次取出最小的值啊,百度搜“C++堆排序小顶堆重载为什么return大于号”,看了几个文章、博客,又百度了什么叫层序遍历,没搞懂,有机会看看优先队列源码吧,不知道咋写的,我想的是首先从小到大排序,升序,用堆的话,看似是小顶堆,但上面说过,升序要用大顶堆不再解释,但我优先队列不要求排序啊,我tm只想获得所有数据里的最小值就够了,下面的咋排序无所谓啊,那也就是建完堆就OK了啊,每次取top堆顶就行了,那小根堆就没问题啊,每次弹出堆顶最小值,新插入的值放队尾,队尾再放到堆顶,这时候顶的下面俩数一定是所有数中最小的,做个比较交换就行了,新加入的顶,大于下面的就换,获得的依旧是最小值,艹写到这我已经懂了。那个文章里的“我们再想想”说的跟这个情况也不一样,那个没有不断插入点,算了学排序再研究吧,但好像朦胧之间又些许明白,姑且理解为小于就换,小于意思是我权值比你大就换,那最后堆顶就是权值小的
私信他,没想到真回复我了,但也没有后续了,但如果我想有偿是可以加上他的

1 q神都不看别人代码。q神和岛娘大一各自在各自空间发东西被他们朋友拉黑如今诧诧风云的人物,想找人有偿能找到但不想再社交了累了
实在没招了,又把POJ的discuss从头到尾从尾到头挨个看了一遍,改了改也没啥用
崩溃
又去把2遍反存图的最朴素方法的代码提交了下,上面留证据里WA那个,做个改动AC了,发现是下面###16,把 cin>>a>>b>>dir[a][b]; 改成 cin>>a>>b>>c; dir[a][b]=c; 就好了,scanf输入带变量直接很明显的错误codeblock就能验证,但cin这么输入洛谷AC,POJ就报错,搞不懂,而且之前说int a,b,c放里面和外面报错不同,现在发现也是这个dir[a][b]引起的
POJ上AC代码(2遍朴素迪杰斯特拉)

1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 #include<queue> 5 using namespace std; 6 int n,m,x; 7 int dir[1001][1001]; 8 int dir_go[1001][1001]; 9 int ans_return[1001]; 10 int ans_go[1001]; 11 int vis[1001]; 12 int main() 13 { 14 while(cin>>n>>m>>x){ 15 memset(dir_go,0x3f,sizeof(dir_go)); 16 memset(dir,0x3f,sizeof(dir)); 17 memset(ans_return,0x3f,sizeof(ans_return)); 18 memset(vis,0,sizeof(vis)); 19 int a,b,c; 20 for(int i=0;i<m;i++){ 21 cin>>a>>b>>c; 22 dir[a][b]=c; 23 dir_go[b][a]=dir[a][b]; 24 } 25 for(int i=1;i<=n;i++){ 26 dir[i][i]=0; 27 dir_go[i][i]=0; 28 } 29 ans_return[x]=0; 30 int p; 31 int mixnum; 32 for(int i=0;i<n;i++){ 33 mixnum=0x3f3f3f3f; 34 for(int j=1;j<=n;j++){ 35 if(ans_return[j]<mixnum&&vis[j]==0){ 36 p=j; 37 mixnum=ans_return[j]; 38 } 39 } 40 vis[p]=1; 41 for(int k=1;k<=n;k++){ 42 if(ans_return[k]>ans_return[p]+dir[p][k]) 43 ans_return[k]=ans_return[p]+dir[p][k]; 44 } 45 } 46 47 memset(ans_go,0x3f,sizeof(ans_go)); 48 memset(vis,0,sizeof(vis)); 49 50 ans_go[x]=0; 51 for(int i=0;i<n;i++){ 52 mixnum=0x3f3f3f3f; 53 for(int j=1;j<=n;j++){ 54 if(ans_go[j]<mixnum&&vis[j]==0){ 55 p=j; 56 mixnum=ans_go[j]; 57 } 58 } 59 vis[p]=1; 60 for(int k=1;k<=n;k++){ 61 if(ans_go[k]>ans_go[p]+dir_go[p][k]){ 62 // cout<<"@ "<<p<<" "<<k<<" "<<ans_go[k]<<endl; 63 ans_go[k]=ans_go[p]+dir_go[p][k]; 64 // cout<<"# "<<p<<" "<<k<<" "<<ans_go[k]<<endl; 65 } 66 } 67 } 68 69 int maxnum=0; 70 for(int i=1;i<=n;i++) 71 if(maxnum<ans_go[i]+ans_return[i]) 72 maxnum=ans_go[i]+ans_return[i]; 73 cout<<maxnum<<endl; 74 } 75 }
又去把n遍暴力迪杰斯特拉提交了下,怎么改都TLE(cin,scanf都是),可见10^9时限2s也是超时的,但有个问题,我洛谷cin写法1.96sTLE,scanf反而是2.16sTLE,离奇
多少也能理解,再把弗洛伊德版本的交下,上面证据里RE那个,依旧是cin >> dir[a][b]的问题,改完TLE,这下舒服了,TLE才对
再继续交堆优化n遍迪杰斯特拉洛谷AC那个,发现也是TLE,舒服了,我这强迫症哎,提示WA或者RE就必须给他调出来,难道n*n*logn也不行嘛???(scanf也是TLE),洛谷是1.64sAC,10^6都要这么久啦?莫非输入了100评测组数据

逐渐进入状态,大后期,这段时间感觉真正的活,自己的脑子,很少再因为左右女生导致以为看自己,学习就目光狭窄的脑子都紧绷的,很别扭,现在可以专注了,状态很好,这个人学习的时候旁边有女生也可以忽视,自己专注,很灵活,性欲依旧很强,但白天学习的时候可以很好的控制,看到维族美女也没兴趣了。进入完全专注入定状态,听不到周围噪音(如果有的话),新疆图书馆真震撼,内地没有过,周末爆满md没位置,都蹲地下学,氛围条件一级棒,清华图书馆也不过如此把
起初没发现是dir[a][b]问题之前我发现poj用优先队列就WA,惊呆了,去查了好多优先队列的博客,也没发现啥问题
dir[a][b]问题是POJ总RE给我干崩溃了,我提交到洛谷提示的也是RE,翻译出来是
received signal 11: segmentation fault with invalid memory reference 接收信号11: 内存引用无效的分段错误
才发现是scanf的问题,因为已经没有其他可以改的了,在我翻POJdiscuss的时候我还改过memset,用for,改过最大值,从0x1~7f试了个遍,最后两遍的memset0x3fAC后就没这个顾虑了,附上什么都改的代码

1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 //#include <functional> 5 #include<queue> 6 using namespace std; 7 int n,m,x; 8 int dir[1005][1005]; 9 struct Node{ 10 int num; 11 int weight; 12 }; 13 int ans[1005][1005]; 14 //priority_queue<Node>q; 15 priority_queue<Node >q; 16 //bool operator<(Node a ,Node b){ 17 // return a.weight>b.weight; 18 //} 19 20 bool operator < (Node a ,Node b){ 21 if(a.weight == b.weight) 22 return a.num > b.num; 23 return a.weight > b.weight; 24 } 25 Node firstnode; 26 Node thisnode; 27 Node nextnode; 28 int main() 29 { 30 while(scanf("%d%d%d",&n,&m,&x)!=EOF){ 31 // while(cin>>n>>m>>x){ 32 // while(!q.empty()) 33 // q.pop(); 34 // for(int i=0;i<1002;i++) 35 // for(int j=0;i<1002;j++){ 36 // dir[i][j]=0x7ffffff; 37 // ans[i][j]=0x7ffffff; 38 // } 39 memset(dir,0x3f,sizeof(dir)); 40 memset(ans,0x3f,sizeof(ans)); 41 // int a,b; 42 int a,b,c; 43 for(int i=0;i<m;i++){ 44 scanf("%d%d%d",&a,&b,&c); 45 dir[a][b]=c; 46 // cin>>a>>b>>dir[a][b]; 47 } 48 for(int i=1;i<=n;i++){ 49 dir[i][i]=0; 50 ans[i][i]=0; 51 } 52 for(int i=1;i<=n;i++){ 53 firstnode.num=i; 54 firstnode.weight=0; 55 q.push(firstnode); 56 while(!q.empty()){ 57 thisnode=q.top(); 58 for(int j=1;j<=n;j++){ 59 if(ans[i][j]>thisnode.weight+dir[thisnode.num][j]){ 60 ans[i][j]=thisnode.weight+dir[thisnode.num][j]; 61 nextnode.num=j; 62 nextnode.weight=ans[i][j]; 63 q.push(nextnode); 64 } 65 } 66 q.pop(); 67 } 68 } 69 int maxnum=0; 70 for(int i=1;i<=n;i++){ 71 if(i==x) 72 continue; 73 if(ans[i][x]+ans[x][i]>maxnum) 74 maxnum=ans[i][x]+ans[x][i]; 75 } 76 printf("%d\n",maxnum); 77 // cout<<maxnum<<endl; 78 } 79 }
回头想,发先AC的只有2遍朴素的,难道我优先队列有问题,涉及他的就有问题,不敢心再写下2遍反存图,且优先队列的迪杰斯特拉,返回个WA,干他奶奶的真漂亮,彻底没脾气了,对拍跑了这么久依旧没发现错误,再跑下去电脑又蓝屏了。对拍代码

1 #include<stdio.h> 2 #include<iostream> 3 #include<time.h> 4 using namespace std; 5 6 int main(){ 7 srand(time(0) + (unsigned long long)(new char)); 8 int n = rand()%1000+1; 9 int m = rand()%100000+1; 10 int x=rand()%n+1; 11 cout<<n<<" "<<m<<" "<<x<<endl; 12 for(int i=0;i<m;i++){ 13 int a=rand()%n+1; 14 int b=rand()%n+1; 15 int c=rand()%1000+1; 16 cout<<a<<" "<<b<<" "<<c<<endl; 17 } 18 }
改啊改,最后发现麻痹的我提交错题了艹,3268Silver Cow Party,3278Catch That Cow,都是奶牛艹,我上个厕所回来poj退出登录了,Prob.ID的搜索框里之前刷过3278,我一看有提示误以为这个题是3278,帖了一堆
3268提交了一大堆3278
综上推测,POJ数据很水很垃圾,但评测的数据很多组,导致拥有强测试数据的洛谷AC的时候,poj会TLE,且cin在洛谷上AC,在poj可能会TLE
还有就是scanf的话,数组下边必须确定,否则哪都WA,但cin在洛谷可以数组下标由输入确定,poj就会根据声明定义变量是局部还是全局,而随机报TLE/WA/RE
比如int a,b,c; cin>>a>>b>>dir[a][b];在poj直接让你调到崩溃
终极代码,2遍反存图,堆优化迪杰斯特拉,discuss说那么多人0ms,没去研究搁置吧
AC代码
1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 #include<queue> 5 using namespace std; 6 int n,m,x; 7 int dir[1001][1001]; 8 int dir_go[1001][1001]; 9 struct Node{ 10 int num; 11 int weight; 12 bool operator<(const Node &a)const{ 13 if(weight==a.weight) 14 return num > a.num; 15 return weight>a.weight; 16 } 17 }; 18 int ans[1001][1001]; 19 int maxnum=0; 20 int a,b,c;//放里面就WA 21 int ans_go[1010][1010]; 22 priority_queue<Node>q; 23 Node firstnode; 24 Node thisnode; 25 Node nextnode; 26 int main() 27 { 28 while(scanf("%d%d%d",&n,&m,&x)!=EOF){ 29 while(!q.empty()) 30 q.pop(); 31 memset(dir,0x3f,sizeof(dir)); 32 memset(ans,0x3f,sizeof(ans)); 33 memset(ans_go,0x3f,sizeof(ans_go)); 34 memset(dir_go,0x3f,sizeof(dir_go)); 35 for(int i=0;i<m;i++){ 36 scanf("%d%d%d",&a,&b,&c); 37 dir[a][b]=c; 38 dir_go[b][a]=c; 39 } 40 for(int i=1;i<=n;i++){ 41 dir[i][i]=0; 42 ans[i][i]=0; 43 dir_go[i][i]=0; 44 ans_go[i][i]=0; 45 } 46 47 // int i=x; 48 firstnode.num=x; 49 firstnode.weight=0; 50 q.push(firstnode); 51 while(!q.empty()){ 52 thisnode=q.top(); 53 q.pop(); 54 for(int j=1;j<=n;j++){ 55 if(ans[x][j]>thisnode.weight+dir[thisnode.num][j]){ 56 ans[x][j]=thisnode.weight+dir[thisnode.num][j]; 57 nextnode.num=j; 58 nextnode.weight=ans[x][j]; 59 q.push(nextnode); 60 } 61 } 62 } 63 // while(!q.empty()) 64 // q.pop(); 65 66 firstnode.num=x; 67 firstnode.weight=0; 68 q.push(firstnode); 69 while(!q.empty()){ 70 thisnode=q.top(); 71 for(int j=1;j<=n;j++){ 72 if(ans_go[x][j]>thisnode.weight+dir_go[thisnode.num][j]){ 73 ans_go[x][j]=thisnode.weight+dir_go[thisnode.num][j]; 74 nextnode.num=j; 75 nextnode.weight=ans_go[x][j]; 76 q.push(nextnode); 77 } 78 } 79 q.pop();//这个顺序无影响 80 } 81 maxnum=0; 82 for(int i=1;i<=n;i++){ 83 if(maxnum<ans[x][i]+ans_go[x][i]) 84 maxnum=ans[x][i]+ans_go[x][i]; 85 } 86 printf("%d\n",maxnum); 87 } 88 } 89 90 //之前qq也加过邝斌,是一个橙色的太阳头像,叫bin
小知识点:
###2:王秋献:吃七个烧饼饱了不是第七个饱的,是有前六个做铺垫,所以不能说那些朦胧的博客写的不好,可能也是奠定了基础
###3:关于搜索的一些总结,搞混淆了,之前记得广搜复杂度是n*m其实是行*列,其实就是所有点,现在看博客都说BFS复杂度是边+点,奇怪了,边也算吗?搜的不都是点吗?算了复杂度的都先搁置
###4:这里logN实际是多少其实跟底数没关系,底可以是任意,和N无关,参考博客、参考博客、换底公式
###5:回顾:a^n=b中,a底数,n指数,b幂。log(a)(b)=n,a底数,b真数,n叫“以a为底b的对数”,log(a)(b)函数叫做对数函数。ln底数是e,2.71848,lg底数是10
###6:log(n!)=Θ(n·log(n))吗、Θ、为什么排序算法的复杂度不可能小于O(NlogN)、某人的堆排序复杂度论证
###7:堆排序底数是2,上面精品推导里也说是2
###8:堆排序本质以及迪杰斯特拉用到的堆排序的哪个部分:
有个很大的疑问,按照堆排序后并不是从大到小的啊,左右子树大小没有要求,百度搜索结果好多回答解释的狗屁不通
先看这个,后看这个终于明白了,其实就是堆排序首先建立堆,经过若干次调整完成建堆,具体多少次上面精品推导里有说,反正到这复杂度就是n了,但注意只是大顶堆or小顶堆,左右子树顺序不确定还要再做调整,咋调整呢,就是取出堆顶最大值,记录保存,最后后面的叶子节点放在堆顶,向下沉,这个沉是logn,因为最多就logn层,那总共n个数,就是nlogn,所以tmd不是一直以为的n*nlogn,其实是n+nlogn,所以取最大复杂度就是nlogn
###9:现在发现hdu11页就是坑货,谁再提hdu11JB给他打断。无意间搜了下自己几年前在博客园和CSDN写的博客,真他么一坨屎,瞎写瞎刷毫无模块规律,西安培训的qq文档也没啥必去找了多年前那些,该不懂的还是不懂,艹。什么最大上升子序列,字符串专题,这几年都tm活到狗身上了
###10:另一个平台很垃圾,简直套壳poj,poj挂他也挂,且都会告诉你哪个点挂了
###11:codeforce,现在感觉越来越爱帮人找bug了,洛谷讨论区求改bug的
###12:电脑好几天没关,一直用反而没3F0放电问题了
###13:发现对拍只是赛场上写完优化代码后,用暴力跟他对拍检验对不对的,而不是用来WA后看数据的,╮(╯▽╰)╭我只能用对拍看数据,实在找不出哪里有问题

写着代码无法专注脑子里都是周围女生sq。每次很简单的问题总是要找bug找好久,好讨厌这样的自己。邓思雨:不用vs因为比赛不用vs,西安培训区域赛出题人不透题的数一巨巨
###14:UVA和USACO区别,USACO是美国中学生大学生的比赛网站、UVA是西班牙大学的刷题网站 据说如果你 能在UVA上AC一千道题以上,就尽管向IBM、微软什么的发简历吧,绝对不会让你失望的 ,参考博客,USACO可以看数据??
###15:真有意思,有点可爱,没读懂题就写,下面人还能看懂,真牛,踩在脑袋上挣扎反抗过了那个阶段
POJ讨论里给的数据也没问题
###16:百度智能回答真棒
cin改成scanf的时候遇到了问题,这么写可以,
for(int i=0;i<m;i++) cin>>a>>b>>dir[a][b];
这么写就不行了
for(int i=0;i<m;i++) scanf("%d%d%d",&a,&b,&dir[a][b]);
###18:

曾经问过的人,往事不堪回首,吴师兄,纸盒。按眼睛上眼框通明,视力也好了
###19:
无意间看到曾经听说的hihicode,CCCC天梯赛贴个链接吧