UVa 1344 Tian Ji The Horse Racing
题目描述
这是一个源自中国历史"田忌赛马"故事的问题。田忌和齐王各自有 nnn 匹马,每匹马有一个速度值。比赛规则是进行 nnn 轮比赛,每轮各派出一匹马,每匹马只能使用一次。每场比赛胜者获得 200200200 银元,败者失去 200200200 银元,平局则没有银元得失。
已知齐王在每个等级的马都比田忌的强,但通过孙膑的计策,田忌可以调整马的出场顺序来最大化收益。现在需要编写程序计算田忌最多能获得多少银元。
题目分析
问题本质
这个问题可以看作是一个特殊的二分图最大权匹配问题:
- 将田忌的马放在左侧,齐王的马放在右侧
- 如果田忌的某匹马能赢齐王的某匹马,则在它们之间建立一条权值为 111 的边
- 如果会输,则权值为 −1-1−1
- 如果平局,则权值为 000
但由于马的速度决定了胜负关系,且双方马匹都按速度排序后,这个问题有更高效的贪心解法。
贪心策略
核心思想是"用最弱的马去消耗对方最强的马,用最强的马去赢对方次强的马"。
具体步骤:
- 将双方的马都按速度从大到小排序
- 设置四个指针,分别指向双方当前最快和最慢的马
- 比较策略:
- 如果田忌最快的马能赢齐王最快的马:直接比赛,田忌赢一场
- 如果田忌最快的马输给齐王最快的马:用田忌最慢的马对齐王最快的马,输一场减少损失
- 如果双方最快的马速度相等:
- 如果田忌最慢的马能赢齐王最慢的马:直接比赛最慢的马,田忌赢一场
- 否则:用田忌最慢的马对齐王最快的马,可能输或平
算法复杂度
- 排序:O(nlogn)O(n \log n)O(nlogn)
- 贪心匹配:O(n)O(n)O(n)
- 总复杂度:O(nlogn)O(n \log n)O(nlogn),对于 n≤1000n \leq 1000n≤1000 完全可行
代码实现
// Tian Ji The Horse Racing
// UVa ID: 1344
// Verdict: Accepted
// Submission Date: 2025-10-20
// UVa Run Time: 0.010s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net#include <iostream>
#include <algorithm>
#include <vector>using namespace std;int main() {int n;while (cin >> n && n != 0) {vector<int> tian(n), king(n);for (int i = 0; i < n; i++) cin >> tian[i];for (int i = 0; i < n; i++) cin >> king[i];// 将双方马匹按速度从大到小排序sort(tian.begin(), tian.end(), greater<int>());sort(king.begin(), king.end(), greater<int>());// 初始化指针:tianFast/kingFast指向当前最快的马,tianSlow/kingSlow指向当前最慢的马int tianFast = 0, tianSlow = n - 1;int kingFast = 0, kingSlow = n - 1;int win = 0, lose = 0; // 记录赢和输的场次// 贪心匹配过程while (tianFast <= tianSlow) {// 情况1:田忌最快的马能赢齐王最快的马if (tian[tianFast] > king[kingFast]) {win++; // 田忌赢一场tianFast++; // 双方最快的马出局kingFast++;}// 情况2:田忌最快的马输给齐王最快的马else if (tian[tianFast] < king[kingFast]) {lose++; // 田忌输一场tianSlow--; // 田忌用最慢的马对齐王最快的马kingFast++; // 齐王最快的马出局}// 情况3:双方最快的马速度相等else {// 比较双方最慢的马if (tian[tianSlow] > king[kingSlow]) {// 田忌最慢的马能赢齐王最慢的马win++; // 田忌赢一场tianSlow--; // 双方最慢的马出局kingSlow--;} else {// 用田忌最慢的马对齐王最快的马if (tian[tianSlow] < king[kingFast]) {lose++; // 只有田忌最慢的马比齐王最快的马慢时才算输}tianSlow--; // 田忌最慢的马出局kingFast++; // 齐王最快的马出局}}}// 计算最终收益:(赢的场次 - 输的场次) × 200int result = (win - lose) * 200;cout << result << endl;}return 0;
}
关键点说明
- 排序方向:从大到小排序便于比较最快的马
- 指针移动:四个指针清晰地表示了当前可用的马匹范围
- 平局处理:当最快的马速度相同时,需要根据最慢马的情况决定策略
- 收益计算:只有赢和输影响最终结果,平局不影响银元数
这个贪心算法能够正确解决问题,因为它在每一步都做出了局部最优的选择,从而保证了全局最优解。