UVA1395 苗条的生成树 Slim Span
求一棵生成树,使它的最大边权与最小边权之差最小。
假设已经求出了一棵最小生成树,它的最小边权一定是图上所有边中最小的;它的最大边也应该是相对最小的。
也就是说,不改变当前最小边,它的边权差只能变大,答案不可能变得更优了。
那么想要答案更新,只可能是最小边变大,最大边也变大。
那么删去最小边,加入比当前树上最大的边稍大的边,如果图依旧联通,就可以检查是否更新答案。
但是这样...想要检查图是否联通,复杂度是O(n*m)的。这样总复杂度就是O(n*m^2)的,显然不行。
思考一下,想删除最小边,不如把这条边标记为不可用后,重新跑一边最小生成树。
最小生成树的复杂度是O(nlogn),每条边删除一次就是O(m*nlogn)。
其实整理一下,就是枚举每一条边作为最小边,更新答案就可以了。
代码如下
#include<cstdio> #include<iostream> #include<cmath> #include<cstring> #define MogeKo qwq #include<algorithm> using namespace std; const int maxn = 1e6; const int INF = 0X3f3f3f3f; int n,m; int ans,fa[maxn]; struct edge { int u,v,val; bool operator < (const edge & N)const { return val < N.val; } } e[maxn]; int getfa(int x) { if(fa[x] == x) return x; return fa[x] = getfa(fa[x]); } void init() { for(int i = 1; i <= n; i++) fa[i] = i; } void kruskal(int s) { init(); int mx,mn; int cnt = 0; for(int i = s; i <= m; i++) { int x = getfa(e[i].u); int y = getfa(e[i].v); if(x == y)continue; fa[x] = y; cnt++; if(cnt == 1)mn = e[i].val; if(cnt == n-1)mx = e[i].val; } if(cnt < n-1)return; ans = min(ans,mx-mn); } int main() { while(scanf("%d%d",&n,&m)) { if(!n && !m)break; ans = INF; for(int i = 1; i <= m; i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].val); sort(e+1,e+m+1); for(int i = 1; i <= m; i++) kruskal(i); if(ans == INF) printf("-1\n"); else printf("%d\n",ans); } return 0; }