1 条题解
-
0
题目大意
有 个工程师,编号 到 。
完成一个项目需要至少 单位工作量。
如果项目完成,第 个工程师获得奖金 ,但他若完成 单位工作,则收益为其中 是他每单位工作的成本。
如果项目未完成,所有人收益为 。
每个工程师依次宣布自己完成的工作量 ,目标是最大化自己的收益。
若预期收益为负,则选择 。
已知所有 ,求最终每个工程师宣布的 。关键观察
. 个人最大可接受工作量
$$a_i - c_i b_i \ge 0 \quad\Rightarrow\quad c_i \le \frac{a_i}{b_i} $$
对工程师 ,若项目完成,则他希望 ,即由于 是整数,定义
$$\text{cap}_i = \left\lfloor \frac{a_i}{b_i} \right\rfloor $$这是该工程师在不亏损的前提下最多愿意完成的工作量。
如果项目完成,他只会选择 ,且当 时收益可能为 (题目允许零收益时仍工作)。. 项目完成条件
设 。- 若 ,则项目完成,所有人获得奖金,收益为 。
- 若 ,则项目失败,所有人收益为 。
工程师只有在确信项目最终能完成时才会付出正的工作量,否则不如不工作(收益 )。
. 逆向归纳(从后向前决策)
因为决策顺序固定且信息完全,可以用逆向归纳求解。- 最后一个工程师(第 个)看到前面 人已经完成了 单位。
若 ,他直接选 。
若 ,他需要决定是否工作。
设剩余需要量 。
若 ,他选择 (刚好使项目完成,收益最大)。
若 ,他无法独自补足缺口,且他知晓后面没人了,因此项目必失败,他选 。 - 倒数第二个工程师(第 个)知道第 个会如上反应。
他可以选择自己的 ,然后预测第 个的响应。
类似分析可知,每个工程师都会在“自己能补足剩余缺口”时恰好补足,否则贡献自己的全部能力 ,让前面的人继续尝试。
这等价于 从后向前贪心:维护当前仍需完成的工作量 ,初始为 。
对于 从 到 :
若 ,则 ,,结束。
否则 ,。 - 如果遍历完所有人后 ,说明所有工程师的总能力 ,项目无法完成,此时所有 都应为 。
算法步骤
. 读入 ,数组 和 。
$$\text{cap}_i = \left\lfloor \frac{a_i}{b_i} \right\rfloor $$
. 计算每个工程师的最大可接受工作量:. 初始化 ,数组 全为 。
. 从 递减到 :- 若 ,跳出循环。
- 令 。
- ,。
. 若最后 ,将 全部置为 (项目无法完成)。
. 输出 。
复杂度分析
- 计算 需要 。
- 逆向遍历一次 。
- 总时间复杂度 ,空间复杂度 。
满足 和 的限制。
正确性说明
该贪心算法完全模拟了逆向归纳的均衡结果:
- 最后一个工程师会在力所能及的情况下恰好完成剩余工作量,否则放弃。
- 前面的工程师知道后面的人会这样行动,因此他们也会在力所能及时填补缺口,否则贡献全部能力。
- 若总能力不足,所有人都得不到奖金,最优选择是 。
- 每个工程师的决策都最大化了自己的收益,且结果唯一。
参考代码(C++)
#include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, k; cin >> n >> k; vector<int> a(n), b(n); for (int i = 0; i < n; ++i) cin >> a[i]; for (int i = 0; i < n; ++i) cin >> b[i]; vector<int> cap(n); for (int i = 0; i < n; ++i) { cap[i] = a[i] / b[i]; // 向下取整 } vector<int> c(n, 0); int rem = k; for (int i = n - 1; i >= 0; --i) { if (rem == 0) break; int take = min(cap[i], rem); c[i] = take; rem -= take; } if (rem > 0) { fill(c.begin(), c.end(), 0); } for (int i = 0; i < n; ++i) { cout << c[i] << " \n"[i == n - 1]; } return 0; }样例验证
样例1
从后向前:- : , ,
- : , ,
- : , ,
输出 ,符合。
样例2
,总能力
最终 ,全部置 ,输出 。样例3
从后向前:- : , ,
- : , ,
- : , ,
输出 ,符合。
总结:本题利用逆向贪心,将多阶段完全信息博弈转化为简单的从后向前填补缺口的过程,时间复杂度 ,是典型的最优策略分配问题。**
- 1
信息
- ID
- 6648
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 1
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者