题意:首先定义Slim为一个带权树中最大权减去最小权的值。给定一个无向图,如果它存在生成树,求其所有生成树中得最小Slim值。
思路:我自己的思路是:二分Slim值。对于每次选定的Slim值,枚举所有可能的生成树。方法为首先将边权值排序,然后设定两个游标i和j,i从0开始向右扫,相应的j为使得w[j]-w[i]<=Slim的最大值,由边权落在这个范围内的边构成图,看其是否连通(因为连通必有生成树);根据当前Slim值能否构成生成树来相应改变二分区间。
看到另一个思路更好(http://blog.youkuaiyun.com/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;
}