B3928 [GESP202312 四级] 田忌赛马交互讲义 · C++

一、题意:能赢几场?

你和田忌各有 $N$ 匹马,每匹都有自己的速度。一对一比赛——速度严格大于对方才算赢。

你可以自由安排出场顺序,问最多能赢几场?

关键:平局不算赢。题目允许你单方面安排所有出场顺序——只需追求最大胜场数即可。

二、核心思想:刚好够用就行

🐎 战术口号
对付田忌最慢的那匹马,用我手里"刚好能赢"的那匹去打—— 赢得轻松,还把强马留给后面!

那如果我最慢的马连田忌最慢的马都打不过呢?那它对谁都赢不了——索性放弃它(必输一场也认了),把"必输"的损失换给最便宜的马承担。

1
双方都排序

把两队的马从慢到快排好。

2
盯田忌最慢的

看我还没出战的最慢的马——能不能赢这匹?

3
能赢:派出去

战绩 +1,双方各换下一匹马继续。

4
赢不了:扔掉

这匹废马留着也没用,弃掉,换我自己更快的一匹再试。

三、🎮 互动演示

下面这块就是赛场!点 下一步 一帧一帧看,或者点 自动播放 让算法自己跑;也可以输入自己的数据试试。

🏇 田忌赛马 · 双指针演示器

已赢 ans
0
已弃
0
我的指针 i
0
田忌指针 j
0
🐎
我 u[i]
VS
🐴
田忌 v[j]
准备出战
u[]
▲ i
田忌v[]
▲ j
玩法提示:输入框里可以填入自定义的速度(空格分隔),点"应用"就会重置演示。试试故意让我方都很弱,看看算法怎么"扔废马"!

四、规则总结

情况含义动作
u[i] > v[j]我能赢赢 +1   i++, j++, ans++
u[i] ≤ v[j]我太弱弃用   i++

任一支队伍的马用完,比赛就结束了。

五、参考代码

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

int u[50005], v[50005];

int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) cin >> u[i];
    for (int i = 0; i < n; i++) cin >> v[i];

    sort(u, u + n);   // 我的马从慢到快
    sort(v, v + n);   // 田忌的马从慢到快

    int i = 0, j = 0, ans = 0;
    while (i < n && j < n) {
        if (u[i] > v[j]) {   // 能赢:派出去,两边各换下一匹
            ans++;
            i++;
            j++;
        } else {             // 赢不了:扔掉这匹废马
            i++;
        }
    }

    cout << ans << endl;
    return 0;
}
代码只有 20 多行:排序 + 一个 while + 一个 if-else,简单到不能再简单!

六、易错点

  1. 必须排序——不排序贪心就完全失效。
  2. 赢是严格大于:u[i] > v[j],不能写 (平局不算赢)。
  3. 循环边界:任一指针越界都要停,所以是 while (i < n && j < n)

七、一句话总结

🎯 排序后双指针:能赢就上,赢不了就把这匹废马扔掉,继续挑战田忌的同一匹慢马。