首页 > 代码库 > Two Famous Companies 【二分+最小生成树】
Two Famous Companies 【二分+最小生成树】
Problem Description
In China, there are two companies offering the Internet service for the people from all cities: China Telecom and China Unicom. They both are planning to build cables between cities. Obviously, the government wants to connect all the cities in minimum costs. So the minister of finance Mr. B wants to choose some of the
cable plans from the two companies and calculate the minimum cost needed to connect all the cities. Mr. B knows that N-1 cables should be built in order to connect all N cities of China. For some honorable reason, Mr. B should choose K cables from the China Telecom and the rest N-1-K cables from the China Unicom. Your job is to help Mr. B determine which cables should be built and the minimum cost to build them. You may assume that the solution always exists.
Input
Each test case starts with a line containing the number of cities N (1 <= N <= 50,000), number of cable plans M (N-1 <= M <= 100,000) and the number of required
cables from China Telecom K (0 <= K <= N-1). This is followed by M lines, each containing four integers a, b, c, x (0 <= a, b <= N-1, a != b, 1 <= c <= 100, x in {0,1} indicating the pair of cities this cable will connect, the cost to build this cable and the company this cable plan belongs to. x=0 denotes that the cable plan belongs to China Telecom and x=1 denotes that the cable plan is from China Unicom.
Output
For each test case, display the case number and the minimum cost of the cable building.
Sample Input
2 2 1
0 1 1 1
0 1 2 0
2 2 0
0 1 1 1
0 1 2 0
Sample Output
Case 1: 2
Case 2: 1
来源: http://acm.hdu.edu.cn/showproblem.php?pid=4253
大意:有电信和联通两家公司在全省所有城市连电线,规定电信的电线数量,使每两个城市能连接起来。问,最少花费?
题解:
这题出得真tmd牛,搞了好久。
首先,虽然规定了电信电线的数量,但实际给出数据有可能超过规定的数量tel,我们要筛选出最少花费的tel条,同时又不能忘记联通电线的花费,因为有可能比电信的便宜,那么可以得出思路:我们需要动态地衡量电信的电线和联通的电线的花费,在确保选出的tel条电信线花费最少的基础上,使得联通选出来的电线花费也最少。
有这么一个办法:我们设法增加或减少电信电线的价格,这里使用使用二分法,电线价格在-100和100之间,动态增加或减少电信电线的价格,然后所有电线从小到大排序,进行最小生成树。多次二分后就能得出的电信线刚好tel条时的花费,再从总花费中减去增加的那部分花费。
#include<stdio.h> #include<string.h> #include<algorithm> #define M 50001 using namespace std; int set[M]; struct node{ int s; int e; int len; int com; }p[M*2+1]; int find(int r){ int i=r; while(set[r] != r){ r = set[r]; } int j; while(i != r){ j = set[i]; set[i] = r; i = j; } return r; } int merge(int a,int b){ int A = find(a); int B = find(b); if(A != B){ set[B] = A; return 1; } return 0; } void init(){ for(int i=0;i<50001;i++){ set[i] = i; } } int cmp(node a,node b){ if(a.len != b.len){ return a.len < b.len; }else{ return a.com < b.com; } } int n,m,tel,sum; int kru(int x){ int tel2 = n-1; sum = 0; int count = 0; for(int i=0;i<m;i++){ if(p[i].com == 0){ p[i].len += x; } } init(); sort(p,p+m,cmp); for(int i=0;i<m;i++){ if(merge(p[i].e,p[i].s)){ sum += p[i].len; count++; tel2 -= p[i].com; } if(count == n-1){ break; } } for(int i=0;i<m;i++){ if(p[i].com == 0){ p[i].len -= x; } } return tel2 >= tel; } int main(){ int cas = 0; while(~scanf("%d %d %d",&n,&m,&tel)){ for(int i=0;i<m;i++){ scanf("%d %d %d %d",&p[i].s,&p[i].e,&p[i].len,&p[i].com); } int l = -100,r = 100,mid = 0,ans = 0; while(l <= r){ mid = (l + r)/2; if(kru(mid)){ l = mid + 1; ans = sum - mid*tel; }else{ r = mid - 1; } } cas++; printf("Case %d: %d\n",cas,ans); } return 0; }
Two Famous Companies 【二分+最小生成树】