二分+(最小生成树)

在中国,有两家公司为所有城市的人们提供互联网服务:中国电信和中国联通。他们俩都计划在城市之间建立电缆。显然,政府希望以最低的成本连接所有城市。因此,财政部长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\n",++c,ans);
    }
} 

 

posted @ 2020-09-29 21:29  哎呦哎(iui)  阅读(179)  评论(0编辑  收藏  举报