给n个点,m条边的图,每条边要么属于a公司,要么属于b公司。要求一颗最小生成树,条件是其中属于a公司的边数为k。
这题做法很巧妙。
要求最小生成树,但有一定限制,搜索、贪心显然都不对。
要是能找到一种合理的控制方法,使得求MST的过程中可以控制a公司边的数量,那样问题就解决了。
所以我们可以人为给a公司的边加上一定的权值,使得其中一些边不得不退出MST的选择范围内。
如果此时求的mst里a公司的边数>k,那么就要增加权值;边数<k时,权值为负。
所以,通过二分边权值,可以使得求得mst里所含a公司的边数逐渐逼近k,此时记录答案,因为一定有解,所以最终一定是所求答案。
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; const int maxm=100010; const int maxn=50010; struct node { int u,v,w,ty; }e[maxm]; int r[maxn],n,m,k,ret,telecom; bool cmpw(node a,node b) { if(a.w!=b.w) return a.w<b.w; return a.ty<b.ty; } int root(int a) { if(r[a]==-1) return a; return r[a]=root(r[a]); } bool kru(int x) { for(int i=0;i<m;i++) if(e[i].ty==0) e[i].w+=x; sort(e,e+m,cmpw); memset(r,-1,sizeof r); int edge=0;telecom=n-1;ret=0; for(int i=0;i<m;i++) { int ra=root(e[i].u); int rb=root(e[i].v); if(ra!=rb) { r[ra]=rb; ret+=e[i].w; telecom-=e[i].ty; if(++edge==n-1) break; } } for(int i=0;i<m;i++) if(e[i].ty==0) e[i].w-=x; return telecom>=k; } int main() { int cas=1; while(~scanf("%d%d%d",&n,&m,&k)) { for(int i=0;i<m;i++) scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].w,&e[i].ty); int l=-100,r=100,mid,ans=0x3f3f3f3f; while(l<=r) { mid=(l+r)/2; if(kru(mid)) l=mid+1,ans=ret-mid*k; else r=mid-1; } printf("Case %d: %d\n",cas++,ans); } return 0; }
时间: 2024-10-10 14:59:21