1. 题目背景与核心问题解析
P1080 [NOIP 2012 提高组] 国王游戏是一道经典的贪心算法与高精度计算结合的题目。题目描述了一个国王与大臣们玩牌的游戏场景:每位大臣左右手上各写有一个正整数,国王始终排在队伍最前面。当所有大臣排成一列时,每位大臣获得的奖励等于前面所有人左手数字的乘积除以自己右手数字的商。我们需要找到一种排列顺序,使得获得最多奖励的大臣获得的奖励尽可能小。
这个问题的核心在于:
- 排列顺序会影响最终的最大奖励值
- 需要处理大数乘法(左手数字连乘)和除法运算
- 必须证明贪心策略的正确性才能保证算法有效性
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 贪心策略的推导与证明
2.1 相邻交换法的思考过程
考虑相邻的两位大臣i和j:
- 原顺序:... Li, Lj ... Ri, Rj ...
- 交换后:... Lj, Li ... Rj, Ri ...
我们需要比较这两种排列下两位大臣获得奖励的最大值。经过推导可以发现,当Li×Ri < Lj×Rj时,交换后的最大值不会比原顺序更大。因此,我们应该按照左右手数字乘积升序排列大臣。
2.2 严格的数学证明
设前面所有人的左手乘积为S:
- 原顺序时i的奖励:S/Ri
- 原顺序时j的奖励:(S×Li)/Rj
- 交换后i的奖励:(S×Lj)/Ri
- 交换后j的奖励:S/Rj
我们需要保证max((S×Lj)/Ri, S/Rj) ≤ max(S/Ri, (S×Li)/Rj)。通过不等式推导可以证明,当Li×Ri ≤ Lj×Rj时,交换不会使最大值增大。
3. 高精度计算的实现细节
3.1 大数存储与乘法实现
由于左手数字连乘可能非常大(最多1000个10000相乘),必须实现高精度乘法:
cpp复制vector<int> multiply(vector<int>& a, int b) {
vector<int> c;
int t = 0;
for (int i = 0; i < a.size() || t; i++) {
if (i < a.size()) t += a[i] * b;
c.push_back(t % 10);
t /= 10;
}
return c;
}
