poj 3522 二分+判图连通(最小Slim生成树)

题意:首先定义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;
}


你可能感兴趣的:(poj 3522 二分+判图连通(最小Slim生成树))