Codeforces 799C 暴力 线段树 RMQ

通过贪心算法和线段树RMQ解决购物权重最大化问题,利用两种货币购买商品以获得最大权重和。

传送门:题目

题意:

一个人,想要购买两件商品,有两种货币,每件商品能且之能用一种货币购买,每件商品都有一个权重,我们想让购买的商品权重和最大,要求输出最大的权重和,如果没办法购买两件商品,输出0

题解:

第一开始看到数据范围 105 10 5 ,想到这道题得用线段树了。然后突然想到先按权重从大到小排,然后找到能够购买的权重最大的商品,找到后直接break就行了(贪心),这样复杂度到不了 n2 n 2 ,然后就暴力写了一下,果然AC了。

线段树写法:

后来又用线段树RMQ写了一下,也很简单的,我直接套了个模板上去。
然后注意一下,先Query然后Update,这样可以避免Query到自己。

AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define debug(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
using namespace std;

const int maxn = 100010;
struct Node {
    int beauty, cost;
    bool operator<(const Node& rhs)const {
        return beauty > rhs.beauty;
    }
} nodec[maxn], noded[maxn];
int main(void) {
    char ch;
    int i, j, n, C, D, nc = 0, nd = 0, t1, t2, ans = 0,indexc=0,indexd=0;
    //C D是拥有的资产,nc和nd是类别为c或d商品的总数量,t1t2是临时变量,indexc和indexd是记录能够购买的魅力值最大商品的索引值
    cin>>n>>C>>D;
    for (int i = 0; i < n; i++) {
        cin>>t1>>t2>>ch;
        if (ch == 'C')
            nodec[nc].beauty = t1, nodec[nc++].cost = t2;//事实上,这里用vector更简明,但是怕TLE,就没敢用
        else if (ch == 'D')
            noded[nd].beauty = t1, noded[nd++].cost = t2;
    }
    sort(nodec, nodec + nc),sort(noded, noded + nd);//按美丽值从大到小排序

    for (i = 0; i < nc; i++)//下面两个for以贪心的策略找到美丽值最大的可购买的商品
        if (nodec[i].cost <= C) {
            indexc=i,ans += nodec[i].beauty;
            break;
        }
    for ( j = 0; j < nd; j++)
        if (noded[j].cost <= D) {
            indexd=j,ans += noded[j].beauty;
            break;
        }
    if (i == nc || j == nd)//如果只有一件可购买,则把答案重置为0,因为题目要求能且只能购买两件。
        ans = 0;

    for (i = indexc; i < nc; i++)//只用C买两件,看看能不能达到最大
        for (j = i + 1; j < nc; j++)
            if (nodec[i].cost + nodec[j].cost <= C){
                ans = max(ans, nodec[i].beauty + nodec[j].beauty);
                break;//因为我们已经排好序了,所以只要满足条件,我们就可以退出
            }
    for (i = indexd; i < nd; i++)//只用D买两件,看看能不能达到最大
        for (j = i + 1; j < nd; j++)
            if (noded[i].cost + noded[j].cost <= D){
                ans = max(ans, noded[i].beauty + noded[j].beauty);
                break;//因为我们已经排好序了,所以只要满足条件,我们就可以退出
            }
    cout << ans << endl;
    return 0;
}

AC代码(线段树):

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#define debug(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
using namespace std;

const int maxn = 1e5 + 10;
pair<int, int> pii; //first->beauty second->cost
vector<pair<int, int> > c, d;

/******************线段树模板**********************/
int SegTree[maxn << 2];
void BuildTree(int l, int r, int rt) {//建树,lr是总区间,rt是根结点一般为1
    if (l == r) {
        SegTree[rt] = 0; //初始化叶节点
        return ;
    }
    int m = (l + r) >> 1;
    BuildTree(l, m, rt << 1);
    BuildTree(m + 1, r, rt << 1 | 1);
    SegTree[rt] = SegTree[rt << 1] + SegTree[rt << 1 | 1];
}
int Query(int L, int R, int l, int r, int rt) {//区间查询,LR是查询区间,lr是总区间,rt是根结点一般为1
    if (l >= L && r <= R)
        return SegTree[rt];
    int m = (l + r) >> 1;
    int ans1 = 0,ans2=0;
    if (L <= m)
        ans1 = Query(L, R, l, m, rt << 1);
    if (R > m)
        ans2 = Query(L, R, m + 1, r, rt << 1 | 1);
    return max(ans1,ans2);
}
void Update(int point, int value, int l, int r, int rt) {//单点更新,把point点的值改为value,lr是总区间,rt是根结点一般为1
    if (l == r) {
        SegTree[rt] = max(value,SegTree[rt]);
        return;
    }
    int m = (l + r) >> 1;
    if (point <= m)
        Update(point, value, l, m, rt << 1);
    else
        Update(point, value, m + 1, r, rt << 1 | 1);
    SegTree[rt] = max(SegTree[rt << 1] , SegTree[rt << 1 | 1]);
}
/******************线段树模板**********************/
int main(void) {
    char ch;
    int n, C, D, t1, t2, max_cost = 0, ans = 0;
    cin >> n >> C >> D;
    for (int i = 0; i < n; i++) {
        cin >> t1 >> t2 >> ch;
        pii = make_pair(t1, t2);
        ch == 'C' ? c.push_back(pii) : d.push_back(pii);
        max_cost = max(t2, max_cost);
    }
    max_cost = max(max_cost, max(C, D));

    t1 = t2 = 0;//C买一件D买一件
    for (int i = 0; i < (int)c.size(); i++)
        if (C >= c[i].second)
            t1 = max(t1, c[i].first);
    for (int i = 0; i < (int)d.size(); i++)
        if (D >= d[i].second)
            t2 = max(t2, d[i].first);
    if ((t1 & t2) != 0) //t1和t2均不为0
        ans = t1 + t2;

    BuildTree(0, max_cost, 1);//买C两件
    for (auto x : c) {
        t1 = 0;
        if (C > x.second)
            t1 = Query(1, C - x.second, 0, max_cost, 1);
        Update(x.second, x.first, 0, max_cost, 1);
        if (t1 != 0)
            ans = max(ans, x.first + t1);
    }
    BuildTree(0, max_cost, 1);//买D两件
    for (auto x : d) {
        t1 = 0;
        if (D > x.second)
            t1 = Query(1, D - x.second, 0, max_cost, 1);
        Update(x.second, x.first, 0, max_cost, 1);
        if (t1 != 0)
            ans = max(ans, x.first + t1);
    }
    cout << ans << endl;
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值