Codeforces Round #413 C. Fountains 二分

题目链接: Fountains

题目大意:

n座喷泉, 每座喷泉有三个属性, 1. 建造所需要的材料(硬币或钻石), 2. b:美丽值, 3. p:建造所需材料数量, 一个喷泉只用一种材料
你有c个硬币, d个钻石, 建造两座喷泉, 求美丽值最大为多少, 如果无法建造两座喷泉, 输出0

解题思路

分三类情况讨论
1. 一座使用硬币, 一座使用钻石, 直接分别遍历两个集合(硬币建的和钻石建的喷泉), 求出硬币和钻石喷泉在c个硬币和d个钻石下能建造出美丽值的最大值
2. 两座都使用硬币
3. 两座都使用钻石
后两种情况解决思路:
1. 将使用硬币的所有喷泉按照建造花费从小到大排序
2. 在排序的基础上, 预处理mx[j] := 喷泉[0, j]里最大美丽值
3. 遍历所有喷泉(用硬币建造的), 设当前遍历到的喷泉j的花费为p, 美丽值为b, 那么建完这座剩下x个材料;
4. 那么建造的另一座喷泉最大美丽值就是花费小于等于x的所有喷泉中的最大美丽值;
5. 因为喷泉已经排序, 我们可以二分查找x的上界(第一个花费大于x的位置)pos, 那么区间[0, pos-1]里所有喷泉花费小于等于x;
6. 那么mx[pos-1]就是另一座喷泉的最大美丽值
7. (为了防止出现同一座喷泉用了两次的情况, 我们设第一座编号在排序后的数组中编号大于第二座, 二分查找时只在[0, j-1]范围内查找, 这样就不用考虑重复用的情况了)

因为两类做法完全一样, 那么干脆用长度为2的数组记录两类组喷泉的数据, 可以用循环减少代码量

代码

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1E5 + 10;
struct P
{
    int b, p;
    P(int a=0, int b=0) :b(a), p(b) {}
    bool operator<(const P & x) const { return p < x.p; }
}c[2][MAXN];//0为硬币喷泉, 1为钻石
int cnt[2], n, m[2], mx[2][MAXN];

int main()
{
    scanf("%d%d%d", &n, &m[0], &m[1]);//m[0]m[1]为硬币, 钻石数量
    for(int i=0; i<n; ++i)
    {
        int a, b; char ch;
        scanf("%d %d %c", &a, &b, &ch);
        if(ch == 'C') c[0][cnt[0]++] = P(a, b);
        else c[1][cnt[1]++] = P(a, b);
    }

    int ans = 0, M[2] = {0, 0 };//两组各自的最大美丽值
    for(int i=0; i<2; ++i)
        for(int j=0; j<cnt[i]; ++j)
            if(m[i] >= c[i][j].p) M[i] = max(M[i], c[i][j].b);
    if(M[0] && M[1]) ans = M[0] + M[1];

    for(int i=0; i<2; ++i)
    {
        sort(c[i], c[i]+cnt[i]);
        mx[i][0] = c[i][0].b;
        for(int j=1; j<cnt[i]; ++j) mx[i][j] = max(mx[i][j-1], c[i][j].b);
        for(int j=1; j<cnt[i]; ++j)
        {
            int x = m[i] - c[i][j].p;//造完第一座还剩的材料
            int pos = upper_bound(c[i], c[i]+j, P(0, x)) - c[i];//区间[0, pos-1]所有喷泉建造花费小于等于x
            if(pos) ans = max(ans, c[i][j].b+mx[i][pos-1]);//mx[i][pos-1], 第二座最大美丽值
        }
    }
    cout << ans << endl;

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值