2025“钉耙编程”中国大学生算法设计春季联赛(4)

在这里插入图片描述

战斗爽

思路:

首先我们要处理攻击敌人的顺序问题,优先级最高的为血量,之后依次为攻击力和敌人编号

注意同一敌人最多收到k次攻击,这里也需要记录被攻击次数

这里我们可以通过重载优先队列来实现

struct all{
    int at, hp, pos, sk;
};

struct cmp{
    bool operator() (const all a, const all b) {
        if (a.hp == b.hp && a.at == b.at) return a.pos > b.pos;
        else if (a.hp == b.hp) return a.at > b.at;
        return a.hp > b.hp;
    }
};

小华每回合都会收到当前攻击力最强的敌人的伤害,需要Ologn以内的方式定向查找和删除当前击杀的敌人

这里我们选择可重复的multiset

multiset<int> se;
auto it = se.find(a);
if (it != se.end()) se.erase(it);

特别注意,取当前最大攻击元素时的两种取法

auto it = se.rbegin();
auto it = (--se.end());

AC code :

struct all{
    int at, hp, pos, sk;
};

struct cmp{
    bool operator() (const all a, const all b) {
        if (a.hp == b.hp && a.at == b.at) return a.pos > b.pos;
        else if (a.hp == b.hp) return a.at > b.at;
        return a.hp > b.hp;
    }
};

void solve() {   
    int n, u, k, hq; cin >> n >> u >> k >> hq;
    priority_queue<all, vector<all>, cmp> pq;
    int sum = 0, ans = 0;
    
    multiset<int> se;
    for (int i = 1; i <= n; i ++) {
        int a, h; cin >> a >> h;
        se.insert(a);
        pq.push({a, h, i, 0});
    }

    while (!pq.empty() && hq > 0) {
        auto [a, h, p, s] = pq.top();
        pq.pop();

        if (s == k) continue;

        int ca = h - u / (!s ? 1 : 2);
        s ++;

        if (ca > 0) {
            pq.push({a, ca, p, s});
        } else {
            ans ++;
            auto it = se.find(a);
            if (it != se.end()) se.erase(it);
        }
        if (!se.empty()) {
            auto it = --se.end();
            hq -= *it;
        }
    } 
    
    cout << ans << endl;
} 

持家

思路:

很显然先用打折券再用减免券((⊙o⊙)…)

前缀和和前缀积分别处理一下打折券和减免券

枚举用0~k张打折券,其余用减免券的情况,并取最小值即可

特别注意枚举过程中可能会出现其中一种券不足的情况

AC code:

void solve() {   
    int p, n, k; cin >> p >> n >> k;
    vector<int> da;
    vector<int> jian;
    for (int i = 0; i < n; i ++) {
        int t, p; cin >> t >> p;
        if (t) jian.push_back(p);
        else da.push_back(p);
    }
    sort(da.begin(), da.end());
    sort(jian.begin(), jian.end(), greater<int>());
    vector<double> sda;
    double st = 1;
    for (int i = 0; i < da.size(); i ++) {
        st *= 1.0 * da[i] / 10;
        //cout << st << ' ';
        sda.push_back(st); 
    } 
    int o = 0;
    vector<int> sji;
    for (int i = 0; i < jian.size(); i ++) {
        o += jian[i];
        sji.push_back(o);
    } 

    double ans = p;
    for (int i = 0; i <= k; i ++) {
        double ca = p, cb = 0;

        if (!sda.empty() && i > 0) {
            if (i <= sda.size()) ca = p * sda[i - 1];
            else ca = p * sda.back();
        }

        int j = k - i;
        if (!sji.empty() && j > 0) {
            if (j <= sji.size()) cb = sji[j - 1];
            else cb = sji.back();
        }

        ans = min(ans, max(0.0, ca - cb));
    }

    printf("%.2lf\n", ans);

} 

进步

思路:

其实主要就是修改操作如何在满足时间复杂度的情况下修改一个后缀的前缀和

用树状数组或者线段树都可以

AC code:

void update(int x, int val) {
    for (; x <= n; x += x & -x)
        bit[x] += val;
}

int query(int x) {
    int res = 0;
    for (; x; x -= x & -x)
        res += bit[x];
    return res;
}

void solve() {
    cin >> n >> q;
    for (int i = 1; i <= n; i ++)
        cin >> a[i], bit[i] = 0;

    for (int i = 1; i <= n; i++)
        update(i, a[i]);

    int op, x, y, cnt = 0;
    int ans = 0;

    for (int i = 1; i <= q; i ++) {
        cin >> op >> x >> y;
        if (op == 1) {
            int de = y - a[x];
            a[x] = y;
            update(x, de);
        } else {
            cnt++;
            int sr = query(y), sl = query(x - 1);
            int res = sr / 100 - sl / 100;
            ans ^= (res * cnt);
        }
    }

    cout << ans << endl;
}

制衡

思路:

dp

AC code:

void solve() {   
    int n, k;
    cin >> n >> k;
    for (int i = 0; i <= k + 10; i ++) dp[i] = 0;

    for (int i = 1; i <= n; i ++) {
        for (int j = 1; j <= k; j ++) {
            cin >> a[j];
        }
        st[0] = 0;
        for (int j = 1; j <= k; j ++) {
            st[j] = max(st[j - 1], dp[j]);
        }
        for (int j = 1; j <= k; j ++) {
            dp[j] = st[j] + a[j];
        }
    }
    cout << *max_element(dp + 1, dp + k + 1) << endl;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值