Codeforces 230A:Dragons 贪心与排序

题意与词汇

Kirito 初始战斗力为 s,这一关有 n 条龙。第 i 条龙的战斗力是 x_i,击败它后可以获得 y_i 点战斗力加成。

  • strength:战斗力,由整数表示;
  • duel:决斗;
  • equals s:初始战斗力等于 s,这里是在说明初始值;
  • the i-th dragon:第 i 条龙,i 从 1 到 n。

关键规则是严格比较:只有当前战斗力 大于 龙的战斗力,才能获胜。如果当前战斗力不大于 x_i,就不能挑战这条龙。击败后再把奖励加到自己的战斗力上。

贪心思路:先挑战弱的龙

把每条龙看成一组数据 (x_i, y_i),按 x_i 从小到大排序,然后依次挑战:

  1. 如果当前战斗力 s <= x_i,输出 NO;
  2. 否则击败这条龙,并令 s += y_i;
  3. 所有龙都打完,输出 YES。

这样安排不会吃亏:奖励 y_i 不会让战斗力下降,所以先打较弱的龙只会保持或增加后续战斗力。如果连当前最弱的龙都打不过,其他战斗力更高的龙也打不过。题目保证每条龙的奖励非负,因此升序贪心成立。

从 C 思维迁移到 C++

本题把每条龙的两个属性放在一起处理,适合用结构体:

struct Dragon {
    int strength;
    int bonus;
};

再用 vector<Dragon> 保存所有龙。读取时直接给每个结构体成员赋值,排序时传入比较函数。

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

struct Dragon {
    int strength;
    int bonus;
};

bool cmp(const Dragon& a, const Dragon& b) {
    return a.strength < b.strength;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int s, n;
    cin >> s >> n;

    vector<Dragon> dragons(n);
    for (Dragon& dragon : dragons) {
        cin >> dragon.strength >> dragon.bonus;
    }

    sort(dragons.begin(), dragons.end(), cmp);

    for (const Dragon& dragon : dragons) {
        if (s <= dragon.strength) {
            cout << "NO" << endl;
            return 0;
        }
        s += dragon.bonus;
    }

    cout << "YES" << endl;
    return 0;
}

这次卡住的几个 C++ 规则

1. 类型先定义,后使用

cmp 的参数类型是 Dragon,所以编译器在读到比较函数前,必须已经认识 Dragon。最直观的顺序是:

struct Dragon {
    int strength;
    int bonus;
};

bool cmp(const Dragon& a, const Dragon& b) {
    return a.strength < b.strength;
}

如果把 cmp 放在结构体定义前,常见报错是:

'Dragon' was not declared in this scope

2. sort 的第三个参数是比较规则

sort 默认按 < 比较元素,不是只能处理 int 或 string。但 Dragon 是自定义类型,如果没有定义 operator<,编译器就不知道两个 Dragon 谁更小。

因此这里把比较函数作为第三个实参传入:

sort(dragons.begin(), dragons.end(), cmp);

比较函数也可以写成 lambda:

sort(dragons.begin(), dragons.end(),
     [](const Dragon& a, const Dragon& b) {
         return a.strength < b.strength;
     });

两种写法选一种即可。传入自定义比较器后,不需要再为 Dragon 定义 operator<。

3. 比较函数和 sort 调用是分开的

sort(...) 是一次函数调用,调用后用分号结束;不能把比较函数的函数体接在调用后面。

bool cmp(const Dragon& a, const Dragon& b) {
    return a.strength < b.strength;
}

sort(dragons.begin(), dragons.end(), cmp);

4. const Dragon& 表示只读引用

遍历时使用 const Dragon& 不会复制整个结构体,也保证循环中不会意外修改龙的数据。排序比较函数同样用引用接收元素;对这道小题而言按值也能工作,但引用是更通用的写法。

看模板报错时先抓住第一处错误

这次真正有用的信息是最前面的错误,例如:

'Dragon' was not declared in this scope
expected ';' before '{'

先修正第一、第二个错误,再重新编译。后面很长的 vector 或 sort 内部模板报错,往往只是前面语法或类型错误引发的连锁反应。

Windows 编译运行

g++ -std=c++20 230A.cpp -o 230A.exe
.\230A.exe

标准选项应写成 -std=c++20,而不是 -stdc++=20。如果出现 Permission denied,先确认 230A.exe 没有仍在运行;运行中的程序可能占用该文件,导致编译器无法覆盖它。若关闭程序后仍报错,再检查目录写入权限和杀毒软件拦截。

复杂度与记忆主线

  • 排序:O(n log n);
  • 顺序挑战:O(n);
  • 存储所有龙:O(n)。
定义 Dragon
→ 用 vector<Dragon> 读入 (strength, bonus)
→ 按 strength 升序 sort
→ 检查当前战斗力是否严格大于龙的 strength
→ 能打就加 bonus,打不过就 NO
→ 全部打完就是 YES

这题真正练到的不只是贪心,而是 struct → vector<Dragon> → cin 读成员 → sort 自定义比较 → 遍历更新 这一整套 C++ 竞赛写法。