zoukankan      html  css  js  c++  java
  • 二分+(最小生成树)

    在中国,有两家公司为所有城市的人们提供互联网服务:中国电信和中国联通。他们俩都计划在城市之间建立电缆。显然,政府希望以最低的成本连接所有城市。因此,财政部长B先生想从两家公司中选择一些电缆计划,并计算连接所有城市所需的最低成本。 B先生知道应该建立N-1电缆以连接中国所有N个城市。出于某种光荣的原因,B先生应选择中国电信的K电缆,其余选择中国联通的N-1-K电缆。您的工作是帮助B先生确定应构建哪些电缆以及构建电缆的最低成本。您可能会认为解决方案始终存在。
    输入
    每个测试用例均以包含城市数量N(1 <= N <= 50,000),电缆计划M(N-1 <= M <= 100,000)以及中国电信需要的电缆数量K( 0 <= K <= N-1)。随后是M行,每行包含四个整数a,b,c,x(0 <= a,b <= N-1,a!= b,1 <= c <= 100,x in {0,1 }表示该电缆将连接的一对城市,该电缆的制造成本以及该电缆计划所属的公司。x= 0表示该电缆计划属于中国电信,x = 1表示该电缆计划来自中国联通
    输出
    对于每个测试案例,请显示案例编号和电缆构建的最低成本。
    样例输入
    2 2 1
    0 1 1 1
    0 1 2 0
    2 2 0
    0 1 1 1
    0 1 2 0
    样例输出
    情况1:2
    情况2:1
    提示
    在第一种情况下,只有两个城市之间有两个电缆规划,一个来自中国电信,一个来自中国联通。即使成本较高,B先生也需要从中国电信选择一个来满足问题要求。
    在第二种情况下,B先生必须选择中国联通的电缆,答案为1

    大体题意:N个点M边,连n-1条边,其中要有k条联通的边,要求一颗最小生成树

    要求最小生成树,但有一定限制,搜索、贪心显然都不对。

    要是能找到一种合理的控制方法,使得求MST的过程中可以控制a公司边的数量,那样问题就解决了。

    所以我们可以人为给a公司的边加上一定的权值,使得其中一些边不得不退出MST的选择范围内。

    如果此时求的mst里a公司的边数>k,那么就要增加权值;边数<k时,权值为负。

    所以,通过二分边权值,可以使得求得mst里所含a公司的边数逐渐逼近k,此时记录答案,因为一定有解,所以最终一定是所求答案。

    #pragma GCC optimize(2)
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<map>
    #include<string> 
    #include <math.h> 
    #include<memory.h>
    #include<cstring> 
    using namespace std;
    inline int read() {int x=0,f=1;char c=getchar();while(c!='-'&&(c<'0'||c>'9'))c=getchar();if(c=='-')f=-1,c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return f*x;}
    const int maxn=1e6+10;
    //如果此时求的a公司的边数>k,那么就要增加权值;边数<k时,权值为减小 
    struct node{
        int u,v,w,op;
    }a[maxn];
    bool cmp(node x,node y){
        if(x.w!=y.w){
            return x.w<y.w;
        }
        return x.op<y.op;
    }
    int pre[maxn];
    long long res=0;
    int n,m,k;
    //int find(int h){
    ////    return pre[h]==h?h:pre[h]=find(pre[h]);
    //    if(pre[h]==h){
    //        return h;
    //    }
    //    else{
    //        return pre[h]=find(pre[h]);
    //    }
    //}
    int find(int x)
    {
        int pos = x;
        while (x != pre[x]) x = pre[x];
        while (pos != x) {
            int temp = pre[pos];
            pre[pos] = x;
            pos = temp;
        }
        return x;
    }
    int judge(int kk){
        for(int i=0;i<=n;i++){
            pre[i]=i;
        }
        for(int i=1;i<=m;i++){
            if(a[i].op==0){
                a[i].w+=kk;
            }
        }
        sort(a+1,a+m+1,cmp);
        int bian=n-1,edge=0;
        res=0;
        for(int i=1;i<=m;i++){
            int fa=find(a[i].u);
            int fb=find(a[i].v);
            if(fa!=fb){
                pre[fa]=fb;
                res+=a[i].w;
                edge++;
                bian-=a[i].op;
            }
            if(edge==n-1){
                break;
            }
        }
        for(int i=1;i<=m;i++){
            if(a[i].op==0)
                a[i].w-=kk;
        } 
        return bian>=k; 
    } 
    void inint(){
        
    } 
    int main(){
        //0是电信,1是联通 
        int c=0;
        while(~scanf("%d%d%d",&n,&m,&k)){
            for(int i=1;i<=m;i++){
                a[i].u=read(),a[i].v=read(),a[i].w=read(),a[i].op=read();    
            }
            int l=-102,r=102;
            long long ans=0;
            while(r>=l){
                int mid=(l+r)/2;
                if(judge(mid)){
                    l=mid+1;
                    ans=res-mid*k;
                }
                else{
                    r=mid-1;
                }
            }
            printf("Case %d: %lld
    ",++c,ans);
        }
    } 
  • 相关阅读:
    财富感悟:赚大钱的经典语录
    男人成为富翁必备五大特质
    上帝不会辜负每一个有梦的孩子(转)
    一百个绝佳句型
    欢迎光临腾飞品牌网和腾飞悬赏任务网
    金钱在哪里?财富就在你心里
    小本创业30条生意妙经
    告诉你钱途无量的六大创业领域
    告诉你一个简单、深刻的经商道理
    十二条创业经验:包你做梦都赚钱
  • 原文地址:https://www.cnblogs.com/lipu123/p/13654920.html
Copyright © 2011-2022 走看看