1 条题解
-
0
题意翻译
有 个赌场,编号 到 。第 个赌场有三个参数 ,满足 。初始你有 枚硬币。
你可以在当前硬币数 满足 时去赌场 玩一次,之后硬币数变为 。
每个赌场最多去一次,顺序任意。求最终能获得的最大硬币数。核心观察
因为 ,所以每次游戏后硬币数 不会减少(可能不变或增加)。
这意味着随着你进行游戏,可玩的赌场集合只会扩大(因为 是下限, 增大后更多赌场的 会被满足)。因此,贪心策略是:
在当前可玩的赌场中,总是选择 最大的那个。
这可以立即提升 ,且不会破坏未来的可能性(因为更大的 只会让更多赌场变得可玩)。算法步骤
- 将所有赌场按 升序排序。
- 维护一个指针
ptr指向排序后的数组,以及一个最大堆(优先队列)存放当前已解锁的赌场的 值(以及对应的 )。 - 令当前硬币数
cur = k。 - 循环:
- 将
ptr指向的、满足 的所有赌场加入堆中(ptr后移)。 - 从堆中弹出元素(按 从大到小),但 只保留那些 的赌场(因为若 ,赌场已不可玩,直接丢弃)。
- 如果堆为空 → 无法继续,结束循环。
- 否则,取出堆顶(最大 ),令
cur = real_i,继续循环。
- 将
- 输出最终的
cur。
复杂度分析
- 排序 。
- 每个赌场最多入堆一次、出堆一次,堆操作 。
- 总复杂度 每个测试用例,总 不超过 ,可以通过。
正确性证明
引理:在任意时刻,若存在多个可玩的赌场,选择 最大的那个不会比选择任何其他赌场更差。
证明:设当前硬币数为 ,有两个可玩赌场 和 ,且 。
- 若先玩 ,则新硬币数为 。此时 是否仍可玩?因为 且 ( 原可玩),但 可能大于 导致 不可玩。然而,若 ,则 的 ,而 ,故 。这意味着即使先玩 ,新硬币数 仍小于 ,所以 依然可玩(),但 可能因为 不满足 而不可玩?注意 ( 原可玩),且 ?不, 可能小于 ?但 , 可能小于 ?实际上 可能小于 (例如 ),但 成立,所以 玩完后硬币数不变或增加。若 ,则先玩 后 可能因 而不可玩。因此先玩 至少不会比先玩 丢失更多选项,且直接获得更大的 。严格证明可用交换论证:最优解中若第一次未选最大 ,可交换两次操作顺序得到不差的结果。故贪心正确。 ∎
终止性:每次循环要么增加 ( 时严格增加),要么 不变但堆中元素减少(当 时,该赌场被丢弃)。因为 有上界 ,且堆大小有限,算法必然终止。
最优性:贪心过程模拟了不断选取当前最大 的可行操作,最终 达到任何可能序列都无法超越的值(因为任何其他操作顺序得到的 都会被贪心序列在某个步骤中达到或超过)。
代码实现(C++)
#include <bits/stdc++.h> using namespace std; void solve() { int n, k; cin >> n >> k; vector<tuple<int,int,int>> casinos(n); for (int i = 0; i < n; ++i) { int l, r, real; cin >> l >> r >> real; casinos[i] = {l, r, real}; } sort(casinos.begin(), casinos.end()); // 按 l 升序 priority_queue<pair<int,int>> pq; // (real, r) int cur = k; size_t ptr = 0; while (true) { // 加入所有 l <= cur 的赌场 while (ptr < n && get<0>(casinos[ptr]) <= cur) { pq.push({get<2>(casinos[ptr]), get<1>(casinos[ptr])}); ++ptr; } // 移除不可玩的(r < cur) while (!pq.empty() && pq.top().second < cur) { pq.pop(); } if (pq.empty()) break; // 选择 real 最大的 cur = pq.top().first; pq.pop(); // 每个赌场只用一次 } cout << cur << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) solve(); return 0; }
- 1
信息
- ID
- 6640
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者