题目描述
她走的悄无声息,消失的无影无踪。
至今我还记得那一段时间,我们一起旅游,一起游遍山水。到了最终的景点,她却悄无声息地消失了,只剩我孤身而返。
现在我还记得,那个旅游区可以表示为一张由nnn个节点mmm条边组成无向图。我故地重游,却发现自己只想尽快地结束这次旅游。我从景区的出发点(即 1 号节点)出发,却只想找出最短的一条回路重新回到出发点,并且中途不重复经过任意一条边。
即:我想找出从出发点到出发点的小环。
输入格式
每个测试点有多组测试数据
第一行有一个正整数T,(T≤10)表示数据组数
接下来对于每组数据,第一行有两个正整数 n,m,(n,m≤104) 分别代表图的点数和边数
接下来有mmm行,每行三个整数u,v,du,v,du,v,d表示u,vu,vu,v之间存在一条长度为 d,(d≤103)的路径
保证不存在重边,自环。
输出格式
对于每组测试数据,输出题目中所求的最小环的长度。
无解输出 -1
背景
看完这题我得出了一个结论: 矫情的人是不会有女朋友的。
我以为我打完t1之后就可以切掉t2,但实际上。。。。
t2是屑。
思路分析
虽然这题没啥思维难度,但是没思路的还是跟着我再梳理一遍吧。
首先,我们读题发现这题要求最小环。
?????我没学过这种神犇犇算法,你呢?
但是,出题人还没闲高明到自创算法来考你。
所以这一定是学过的算法。
跟最小环有关的算法是什么呢?
emmmm。。。图论啊?最短路?
谈起图论,最基础的就是各种最短路的求法,什么迪姐斯特拉啊,什么死帕法啊,什么弗洛易得啊?全都是图论最熟悉的东西。
最 (最)√
短 (小)√
路 (环) ×
还不错,匹配了两个,想必这两个肯定有关系 【瞎扯
那么我们来仔细回顾一下,原话是怎么说的
找出从出发点到出发点的小环。
既然起点(终点)已经确定了,那么我们不妨画个图来看看。
这可是我辛辛苦苦手画出来的图啊,HZ条件好艰苦啊,我之前都画图工具画的又快又好啊,苦苦
好的我们看这张图,假设边权都是1.
那么怎么才能找到 { 1,2, 4,5 } 这个集合呢?
我们发现,其实我们只要跑2和4之间的最短路,再加上1 就是这个集合。
那我们再画几张图观察一下。
。。。。。。。。。。。。。
。。。。。。。。。。。。。
有没有发现一个结论?
在与1相连的节点中跑最短路,有且只有一条最短路是被包含在最小环中的。【相等什么的忽略不计
那么最小环的长度,就是这条最短路的长度加上 两个节点与1之间的距离。
那好,我们来尝试使用一下这个结论。
我们记录和1相连的所有节点,存在 yh [ i ] 数组中。
建边的时候,直接把与1相连的边全部 ban 掉。
然后分别记录每两个之间的最短路,加上yh与1之间的距离,取最小,即为答案。
这里注意,最好不要SPFA。
细节问题
当你写完上面的做法的时候,好,样例过了就是过了,但是实际上并没有,你TLE了。
??????????
??????????
算法没出错啊?究竟是哪里的问题呢?
仔细想想,复杂度不太对啊?最大可能 n2logn ?【如果不对请留言告诉我
这不就完全炸掉了吗?
那怎么办啊?
这个时候来仔细想想,究竟是什么地方出现了问题呢?
由于建的是双向边,那么我们跑 最短路的时候很可能就不断的在重复。
怎么样才能解决掉这个问题呢?
这里介绍一个玄学优化 【正常人想不到就很正常
我们对于每一个与1相连的点进行 二进制拆分
这样我们的点就可以被被分成好多个两组。
一组做起点,一组做终点,在这两组之间跑最短路。
写对就AC了,加油吧!
代码实现
#include<iostream> #include<cstdio> #include<cmath> #include<queue> #include<cstring> using namespace std; struct ss{ int to,nxt,val; }jd[80010]; int t,n,m,dis[10010],yh[10010],cnt; int head[10010],ecnt,vis[10010],dis1[10010],ans; int cnt1,ed[10010]; priority_queue<pair<int,int> >q;
inline void add(int a,int b,int c) { jd[++ecnt].to=b; jd[ecnt].nxt=head[a]; jd[ecnt].val=c; head[a]=ecnt; return; } void clear() { cnt=0; ecnt=0; memset(head,0,sizeof head); memset(dis,0x3f,sizeof dis); memset(yh,0,sizeof yh); ans=100000000; return; } void dij() { while(!q.empty()) { while(vis[q.top().second]&&!q.empty()) q.pop(); if(q.empty()) break; int u=q.top().second; q.pop(); vis[u]=1; for(int v=head[u];v;v=jd[v].nxt) { int y=jd[v].to; int z=jd[v].val; if(vis[y]) continue; if(dis1[y]>dis1[u]+z) { dis1[y]=dis1[u]+z; q.push(make_pair(-dis1[y],y)); } } } return; } int main() { scanf("%d",&t); while(t--) { clear(); scanf("%d%d",&n,&m); int x,y,z; for(int i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&z); if(x==1||y==1){ if(x==1) swap(x,y); yh[++cnt]=x; dis[x]=z; } else{ add(x,y,z); add(y,x,z); } } int k=0; while((1<<k)<=n) k++; for(int i=0;i<k;i++) { memset(dis1,0x3f,sizeof dis1); memset(vis,0,sizeof vis); memset(ed,0,sizeof ed); cnt1=0; while(!q.empty()) q.pop(); for(int j=1;j<=cnt;j++) { if(((1<<i)&yh[j])) { q.push(make_pair(-dis[yh[j]],yh[j])); dis1[yh[j]]=dis[yh[j]]; } else{ ed[++cnt1]=yh[j]; } } dij(); for(int j=1;j<=cnt1;j++) ans=min(ans,dis1[ed[j]]+dis[ed[j]]); } if(ans<100000000) printf("%d\n",ans); else printf("-1\n"); } return 0; }
AC啦!!!!!!!!!!