招行春招第三题

题意:有n个桌腿,要砍掉某些桌腿使得剩下的桌腿能支撑桌子。规定剩下的桌腿中长度最大的桌腿的数量如果超过一半即可支撑桌子。砍掉每个桌腿需要付出代价。求最小的代价和。

原题:http://codeforces.com/contest/557/problem/C

 

首先,按长度排序。

长度为p的桌腿有a[p]个。

要使得长度为p的桌腿为最长,

(1)将所有长于p的桌腿砍光

(2)那么要按照代价从小到大砍掉sum{长度不到p的腿的数量}-(a[p]-1)条腿。还需要枚举p即可。

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1e5+4, INF = 0x7fffffff;

int ocur[maxn], allCost[maxn], sumOcur[maxn], sumCost[maxn], barrel[204];
// ocur[]:每种长度的桌腿总数;
// allCost[]:每种长度的桌腿的代价总和;
// sumOcur[]:桌腿出现次数的前缀和;
// sumCost[]:桌腿代价总和的前缀和;
// barrel[]:对代价的桶排序。
struct leg{
    int len, cost;
    bool operator < (const leg &a)const{
        return len < a.len;
    }
} legs[maxn];

int main(){

    int n;
    while(cin >> n){
        memset(ocur, 0, sizeof(ocur));
        memset(allCost, 0, sizeof(allCost));
        memset(sumOcur, 0, sizeof(sumOcur));
        memset(sumCost, 0, sizeof(sumCost));
        memset(barrel, 0, sizeof(barrel));
        for(int i = 1; i <= n; i++){
            scanf("%d", &legs[i].len);
            ocur[legs[i].len]++;// 记录某种长度的桌腿总数。
        }
        for(int i = 1; i <= n; i++){
            scanf("%d", &legs[i].cost);
            allCost[legs[i].len]+=legs[i].cost;// 记录某种长度的桌腿的总代价。
        }
        sort(legs+1, legs+1+n);// 按桌腿长度进行排序。

        int maxLen = legs[n].len;// 记录桌腿最大长度,用于规定后续枚举的上界。(已排序)
		for(int i = 1; i <= maxLen; i++){// 构造前缀和。
            sumOcur[i] = sumOcur[i-1] + ocur[i];
            sumCost[i] = sumCost[i-1] + allCost[i];
        }
        int result = INF;
        for(int i = 1; i <= n; i++){// 枚举最终的最长桌腿长度。
            int tempRes = sumCost[maxLen] - sumCost[legs[i].len];// 去掉大于(等于)当前枚举值的桌腿所需的代价。(认为要砍掉)
            int anotherLeftPart = ocur[legs[i].len] - 1;// legs[i].len为当前我认为需要留下的最大长度,anotherLeftPart为需要格外“留下”的最大桌腿数量。(定义要求,长度相同且最长的桌腿数需大于其余长度的桌腿)
            for(int k = 200; k >= 1; k--){// 枚举“长度小于当前桌腿长度”的桌腿们的代价。(从大到小)
                if(barrel[k] > 0){//代价为k的桌腿存在(数量不为0)
                    if(barrel[k] <= anotherLeftPart)//留下的数量“未数”且代价很大,肯定要保留
                        anotherLeftPart -= barrel[k];//计算得剩余还需要考虑的“留下”的桌腿数量
                    else{
                        tempRes+=k*(barrel[k] - anotherLeftPart);//计算“超数”要砍掉的部分的代价
                        anotherLeftPart = 0;//需要格外“留下”的最大桌腿数量置零(表示已经找到了 需要格外“留下”的最大桌腿数量)
                    }
                }
            }
            result = min(result, tempRes);//取代价小的
            barrel[legs[i].cost]++;// 延迟添加当前桌腿的代价值,确保每次枚举代价时所检查的代价值都属于“长度小于等于当前桌腿”的桌腿的代价。
        }
        cout << result << endl;

    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/starluqifei/article/details/88638974