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