POJ 3228 Gold Transportation(带权并查集,好题)

参考链接:http://www.cnblogs.com/jiaohuang/archive/2010/11/13/1876418.html

题意:地图上某些点有金子,有些点有房子,还有一些带权路径,问把所有金子运到房子里所要经过最小的最大相邻路是多少。

   也就是如果光用比“最小的最大相邻路”那条路更小的路是无法运完所有金子的,那么可以先对路径排序,然后一条一条取, 看看是否满足条件:即是否所有并查集的根节点的权值f[i]为非负。

思路:首先想到的是,要能运完所有的金子,那么必须任意一个连通器中的储藏室的总容量V大于等于所含有的金子的总量C,也就是V-C>=0。那么就想到用一个数组存储该镇的状态,若是储藏室,则权值为储藏的                量;若为发现金子的地方,则权值为金子的量的负值。这样每次合并的时候,都要更新一下父节点,即父节点的权值要加上子节点的权值。最后根节点的权值即为该集合中V-C的值。

       若根节点的权值为非负,表明该点集中,可以储存宝藏的容量大于等于发现的宝藏,即这个点集能满足要求。

   若根节点的权值为负值,表明该点集中还有一定数量的宝藏需要储存,但是该集合已经没有房间能存储下了,即不能满足条件。

 

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;
const int maxn=210;
int n,m;
int father[maxn],f[maxn]; //f[i]表示第i个镇所拥有的宝藏,如果为负值,表明此镇需要运出-f[i]的宝藏。如果为正值,表明可以储存f[i]的宝藏
int ans;
struct Edge {
    int u,v,w; //u、v是边的两端点,w是边的权值
    bool operator<(const Edge tmp) const{
        return w<tmp.w;
    }
} edge[20010];

void init(){
    for(int i=0;i<=n;i++){
        father[i]=i;
        f[i]=0;
    }
}
int find_root(int x) {
    if(father[x]!=x){
        father[x]=find_root(father[x]);
    }
    return father[x];
}
void Union(int x,int y){
    father[y]=x;
}
//判断是否所有集合都满足根节点的权值大于等于0
bool ok(){
    int flag=1;
    for(int i=1;i<=n;i++){
        if(father[i]==i && f[i]<0){
            flag=0;
            break;
        }
    }
    if(flag)
        return true;
    else
        return false;
}
int main() {
    int store;
    while(scanf("%d",&n)!=EOF) {
        if(n==0)
            break;
        init();
        for(int i=1; i<=n; i++) {
            scanf("%d",&store);
            f[i]-=store;   //需要运出的宝藏
        }
        for(int i=1; i<=n; i++) {
            scanf("%d",&store);
            f[i]+=store;  //可以储存的宝藏,因为发现宝藏和储存宝藏的地方可能为同一个,所以这里累加就行
        }
        scanf("%d",&m);
        for(int i=1; i<=m; i++) {
            scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
        }

        sort(edge+1,edge+m+1);
        int u,v,i=1;
        //从最小的边开始取
        while(i<=m) {
            u=edge[i].u;
            v=edge[i].v;
            int x=find_root(u);
            int y=find_root(v);
            if(x!=y){
                Union(x,y);
                f[x]+=f[y];
                if(ok()){
                    ans=i;
                    break;
                }
            }
            i++;
        }
        if(i==m+1){
            printf("No Solution\n");
        }
        else{
            printf("%d\n",edge[ans].w);
        }

    }
return 0;
}

 

posted @ 2013-09-04 20:08  辰曦~文若  阅读(362)  评论(0编辑  收藏  举报