描述
给定一张无向图,求图中一个至少包含 3 个点的环,环上的节点不重复,并且环上的边的长度之和最小。该问题称为无向图的最小环问题。在本题中,你需要输出最小环的方案,若最小环不唯一,输出任意一个均可。若无解,输出 No solution.。图的节点数不超过 100 。
输入
第一行两个正整数 n,m 表示点数和边数。
接下来 m 行,每行三个正整数 x,y,z ,表示节点 x,y 之间有一条长度为 z 的边。
输出
输出一个最小环的方案:按环上顺序输出最小环上的点。若最小环不唯一,输出任意一个均可。若无解,输出 No solution.
样例输入
5 7
1 4 1
1 3 300
3 1 10
1 2 16
2 3 100
2 5 15
5 3 20
样例输出
1 3 5 2标签
CEOI1999
题解
其实这道题可以用Floyd。
容易知道,树加上一条非树边可以形成环。利用这个性质,我们可以先求出一棵最小生成树,然后通过枚举每一条非树边求出加上它之后所形成的环的大小并尝试更新答案。输出方案时直接在最小生成树上暴力跳就行了。时间复杂度O(mlogn)。
代码:
#include<bits/stdc++.h> using namespace std; #define gc getchar template<typename T>void read(T&cn){ char c;int sig=1; while(!isdigit(c=gc())) if(c=='-') sig=-1;cn=c-48; while( isdigit(c=gc())) cn=cn*10+c-48;cn*=sig; } #define N 100010 #define INF 0x3f3f3f3f int n,m; namespace T{ int num,f[N]; struct node{ int u,v,w,nxt; }e[N<<1]; void add(int u,int v,int w){ e[++num]=(node){u,v,w,f[u]};f[u]=num; e[++num]=(node){v,u,w,f[v]};f[v]=num; } //build graph int dep[N],dis[N],faz[N],siz[N],son[N],top[N]; void dfs1(int u,int fa,int d){ dep[u]=d;faz[u]=fa;siz[u]=1; for(int i=f[u];i;i=e[i].nxt){ int v=e[i].v; if(v==fa) continue; dis[v]=dis[u]+e[i].w;dfs1(v,u,d+1);siz[u]+=siz[v]; if(siz[v]>siz[son[u]]) son[u]=v; } } void dfs2(int u,int st){ top[u]=st; if(!son[u]) return;dfs2(son[u],st); for(int i=f[u];i;i=e[i].nxt){ int v=e[i].v; if(v==faz[u]||v==son[u]) continue; dfs2(v,v); } } int LCA(int x,int y){ int xx=top[x],yy=top[y]; while(xx^yy){ if(dep[xx]<dep[yy]){swap(xx,yy);swap(x,y);} x=faz[xx];xx=top[x]; } return dep[x]<dep[y]?x:y; } //tree dissection } namespace G{ struct node{ int u,v,w; }e[N]; bool operator < (node a,node b){return a.w<b.w;} //graph int fa[N]; int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} int merge(int x,int y){ int xx=find(x),yy=find(y); if(xx==yy) return 0; fa[xx]=yy; return 1; } //Disjoint-Set int vis[N]; void kruskal(){ sort(e+1,e+m+1);int cnt=0; for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++){ int u=e[i].u,v=e[i].v; if(merge(u,v)){cnt++;vis[i]=1;T::add(u,v,e[i].w);} if(cnt==n-1) break; } } //mst int sum=INF,a,b,c; #define pb push_back vector<int>ans; void work(){ read(n);read(m); for(int i=1;i<=m;i++){read(e[i].u);read(e[i].v);read(e[i].w);} kruskal();T::dfs1(1,0,1);T::dfs2(1,1); for(int i=1;i<=m;i++){ if(vis[i]) continue; int u=e[i].u,v=e[i].v; if(T::dep[u]<T::dep[v]) swap(u,v); if(T::faz[u]==v||u==v) continue; int lca=T::LCA(u,v),tmp=T::dis[u]+T::dis[v]-(T::dis[lca]<<1)+e[i].w; if(sum>tmp){sum=tmp;a=u,b=v;c=lca;} } if(sum==INF){puts("No solution.");exit(0);} while(a^c){printf("%d ",a);a=T::faz[a];}printf("%d ",c); while(b^c){ans.pb(b);b=T::faz[b];} if(ans.size()) for(int i=ans.size()-1;~i;i--) printf("%d ",ans[i]); } } int main(){ G::work(); return 0; }