题意:首先定义Slim为一个带权树中最大权减去最小权的值。给定一个无向图,如果它存在生成树,求其所有生成树中得最小Slim值。
思路:我自己的思路是:二分Slim值。对于每次选定的Slim值,枚举所有可能的生成树。方法为首先将边权值排序,然后设定两个游标i和j,i从0开始向右扫,相应的j为使得w[j]-w[i]<=Slim的最大值,由边权落在这个范围内的边构成图,看其是否连通(因为连通必有生成树);根据当前Slim值能否构成生成树来相应改变二分区间。
看到另一个思路更好(http://blog.csdn.net/ascii991/article/details/7464816):只要枚举最小边即可。使用kruskal的贪心法,得到的生成树的最大边必定最小(回忆最大瓶颈树的相应性质),所以最大边与最小边之差最小。
思路1:
#include <stdio.h> #include <string.h> #include <stdlib.h> #define N 105 int g[N][N],first[N],used[N],w[N*N]; struct edge{ int y,w,next; }e[N*N*2]; int n,m,top; int cmp(const void *a,const void *b){ return (*(int *)a) - (*(int *)b); } void add(int x,int y,int w){ e[top].y = y; e[top].w = w; e[top].next = first[x]; first[x] = top++; } void create(int x,int y){ int i,j; memset(first,-1,sizeof(first)); top = 0; for(i = 1;i<n;i++) for(j = i+1;j<=n;j++) if(g[i][j]>=x && g[i][j]<=y) add(i,j,g[i][j]), add(j,i,g[i][j]); } void dfs(int x){ int i; used[x] = 1; for(i = first[x];i!=-1;i=e[i].next) if(!used[e[i].y]) dfs(e[i].y); } int connection(){ int i,res=0; memset(used,0,sizeof(used)); dfs(1); for(i = 1;i<=n;i++) res += used[i]; return res==n; } int main(){ while(scanf("%d %d",&n,&m) && (n+m)){ int i,j,a,b,c,high,low,mid,flag; memset(g, 0, sizeof(g)); for(i = 0;i<m;i++){ scanf("%d %d %d",&a,&b,&c); g[a][b] = g[b][a] = c; w[i] = c; } qsort(w,m,sizeof(int),cmp); create(w[0],w[m-1]); if(!connection()){//如果全图都不连通,那么必然没有生成树;否则必存在生成树 printf("-1\n"); continue; } low = 0,high = 10000; while(low < high){ mid = (low+high)>>1; flag = 0; for(i = j = 0;j<m&&i<=m-n+1;i++){ while(j<m && w[j]-w[i] <= mid) j++; create(w[i],w[j-1]); if(connection()){ flag = 1; break; } } if(flag) high = mid; else low = mid+1; } printf("%d\n",low); } return 0; }
思路2:
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using namespace std; #define clc(s,t) memset(s,t,sizeof(s)) #define N 105 #define INF 0x3fffffff struct edge{ int x,y,w; }e[N*N]; int n,m,top,fa[N]; int cmp(edge a,edge b){ return a.w<b.w; } int find(int x){ if(fa[x] == x) return x; return fa[x] = find(fa[x]); } int kruskal(int s){ int i,j,a,b,low,high; for(i = 1;i<=n;i++) fa[i] = i; low = e[s].w; for(i = 1,j=s;i<n && j<m;j++){ a = find(e[j].x); b = find(e[j].y); if(a!=b){ fa[a] = b; high = e[j].w; i++; } } if(i==n) return high-low; return -1; } int main(){ while(scanf("%d %d",&n,&m) && (n+m)){ int i,j,k,res=INF; for(i = 0;i<m;i++) scanf("%d %d %d",&e[i].x,&e[i].y,&e[i].w); sort(e,e+m,cmp); for(i = j = 0;i<m;i++){ if(e[i].w == j) continue; j = e[i].w; k = kruskal(i); if(k == -1) break; else res = min(res,k); } if(res==INF) printf("%d\n",-1); else printf("%d\n",res); } return 0; }