HDU 4253-Two Famous Companies(二分+最小生成树)

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

Hint

In the first case, there are two cable plans between the only two cities, one from China Telecom and one from 
China Unicom. Mr. B needs to choose the one from China Telecom to satisfy the problem requirement even the cost is higher.
In the second case, Mr. B must choose the cable from China Unicom, which leads the answer to 1.

题目分析

        这个题目在最小生成树的基础上加了一个限制,那就是其中某一种道路必须有k条,先设想一下暴力法如何解题(我认为暴力法最能体现一个题目的解题思路,优化是为了代替暴力法中的某几个高耗时过程),我们需要找到含有k个电信道路的树中的最小生成树,那么为了找到这个最小生成树,我们需要枚举所有的,含有k个电信的道路树,以此来找到含有k个电信道路中的最小生成树。

       那么我们给这个最小生成树中 为电信的道路加上任意权值(对这些道路构建费用的改变),这样就得到了另外一组测试样例(只有道路花费不一样),对于这组新的测试样例,求其在含有k个电信的道路情况下的最小生成树,这个最小生成树一定和没有加权值的最小生成树一样(这里的最小生成树是在含有k个电信道路的情况下的最小生成树)。

       对于这个东西的证明,因为我们在没有加上权值的情况下已经把所有含有k个电信的道路的树全部枚举出来了,那么设想在这些树中对某一些道路增加一样的构建费用,其结果当然是在没有加权值的情况下的最小生成树的消耗小(这里的最小生成树是在含有k个电信道路的情况的最小生成树)。

       好了,以上就是暴力的解题思路,那么我们要如何优化这个暴力法呢?一般人对于没有加上 含有k个电信的情况下 求最小生成树应该很清楚,那么我们就通过找没有这个限制条件下的最小生成树来得到答案。

       由以上证明的那个理论我们知道,对于加上了权值的测试数据和没有加上权值的测试数据,在同样含有k个电信道路的情况下的树中,二者这些树中的最小生成树是一样的。那么,如果加上了权值的道路在没有 “必须含有k个电信道路” 这一条件下得到了最小生成树,这个最小生成树的道路中恰好含有k个电信道路,那么这个最小生成树就是我们需要的答案(再次强调,这里的最小生成树是在含有k个电信道路的情况下的最小生成树)

       具体来说,我们通过给所有的电信道路加上某一个权值得到一组新的测试数据,对于这组测试数据,我们抛弃“含有k个电信道路”这一条件得到最小生成树,如果这个最小生成树中含有k个电信道路,那么这个最小生成树的道路结构(也就是树的结构)就是在没有加这个权值的情况下在含有k个电信道路的树中是最优的结构,也是答案需要的树的结构。

      对于这个题目,不知道是不是我的翻译有问题,这个题目的要求应该是至少含有k个电信道路吧,但是我将刚好为k作为标准得到的结果是错的,而把至少含有k的电信道路作为标准是对的,如果有问题,望指正,谢谢。

代码区

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<sstream>
#include <iterator>
using namespace std;
#define sf(a) scanf_s("%d",&a)
#define sff(a,b) scanf_s("%d%d",&a,&b)
#define sfff(a,b,c) scanf_s("%d%d%d",&a,&b,&c)
#define sffff(a,b,c,d) scanf_s("%d%d%d%d",&a,&b,&c,&d)
#define slf(a) scanf_s("%lld",&a)
#define ssf(str) scanf_s("%s",str)
#define scf(ch) scanf_s("%c",&ch)
#define mem(s,data) memset(s,data,sizeof(s))
#define inf 0x3f3f3f3f
const int max2 = 100 + 10;
const int max3 = 1000 + 10;
const int max4 = 10000 + 10;
const int max5 = 100000 + 10;
typedef long long ll;

typedef struct Edge{
    int s;
    int e;
    int v;
    Edge(int s = 0, int e = 0, int v = 0) :s(s), e(e), v(v) {};
    bool operator<(const Edge& s)const
    {
        return this->v < s.v;
    }
}Edge;

Edge e0[max5], e1[max5];                    //分别代表x = 0 , x = 1 的道路
int set[max5];                        //代表对应下标的城市的父节点
int n, m, k;
void init(int n);
int build(int a, int len0, int len1,int& sum);

int main()
{
    int kCase = 0;
    while(sfff(n,m,k)!=EOF)
    {
        int s, e, v, ok;
        int len0=0, len1=0;            //分别记录x = 0 ,x = 1 的道路的个数
        for(int i = 0 ;i < m ; i++)
        {
            sffff(s, e, v, ok);
            if (ok)
                e1[len1++] = Edge(s, e, v);
            else
                e0[len0++] = Edge(s, e, v);
        }
        sort(e0, e0 + len0);
        sort(e1, e1 + len1);
        int lSide = -100;
        int rSide = 100;
        
        int ans = 0;
        while(lSide <= rSide)
        {
            const int mid = (lSide + rSide) >> 1;
            int sum;
            const int order = build(mid, len0, len1, sum);
            /*if (order == 0)
            {
                ans = sum - mid * k;
                break;
            }*/
             if (order >= 0)        //权值太小,或者刚刚好
                lSide = mid + 1, ans = sum - mid * k;
            else                            //权值太大
                rSide = mid - 1;

        }
        printf("Case %d: %d\n", ++kCase, ans);
    }
    return 0;
}

//初始化父节点,也不知道这个题目怎么想的,居然0也表示城市
void init(int n)
{
    for (int i = 0; i < n; i++)
        set[i] = i;
}

//找到对应城市的根结点
int find(int x)
{
    if (set[x] == x)
        return x;
    return set[x] = find(set[x]);
}

//给电信的每一条道路加上权值a后寻找没有“必须含有k条电信道路”这一限制条件的最小生成树,
//0表示其中含有k个电信道路,1表示权值太小,导致含有电信道路数过多,0表示权值太大
int build(const int a,const int len0,int len1,int& sum)
{
    init(n);
    sum = 0;                    //初始化
    int index0 = 0, index1 = 0;                //分别记录了e0,e1的当前最便宜道路
    int ans = 0;                    //记录使用了多少个e0的道路
    for(int i = 0 ; i < m ; i++)
    {
        Edge temp;
        int flag = 0;                //记录要构建的是何种道路
        if(index0 >= len0 || index1 < len1 && e1[index1].v < e0[index0].v + a) temp = e1[index1++],flag = 1; 
        else temp = e0[index0++];
        int s = find(temp.s);
        int e = find(temp.e);
        if (s != e)
        {
            set[s] = e;
            sum += temp.v;
            if (!flag)            //电信的道路有额外的权值
                sum += a,ans++;
        }
    }
    if (ans == k)return 0;
    if (ans > k)return 1;
    return -1;
}
View Code
posted @ 2019-02-03 21:48  winter-bamboo  阅读(179)  评论(0编辑  收藏  举报