文章

【GESP】C++四级真题 luogu-B3928 [GESP202312 四级] 田忌赛马

GESP C++四级2023年12月真题。本题为一维数组和排序的应用练习,难度⭐⭐★☆☆。本题在洛谷评定为普及-

本质上说,这里的算法策略采用的是贪心策略,属于GESP五级的内容,但是作为四级考生,在忽略所谓对贪心策略理解的情况下,也可以通过自己的思考解决。

luogu-B3928 [GESP202312 四级] 田忌赛马

题目要求

题目描述

你要和田忌赛马。你们各自有 $N$ 匹马,并且要进行 $N$ 轮比赛,每轮比赛,你们都要各派出一匹马决出胜负。

你的马匹的速度分别为 $u_1,u_2,\cdots,u_n$,田忌的马匹的速度分别为 $v_1,v_2,\cdots,v_n$。田忌会按顺序派出他的马匹,请问你要如何排兵布阵,才能赢得最多轮次的比赛?巧合的是,你和田忌的所有马匹的速度两两不同,因此不可能出现平局。

输入格式

第一行一个整数 $N$。保证 $1\le N \le 5\times 10^4$

接下来一行 $N$ 个用空格隔开的整数,依次为 $u_1,u_2,\cdots,u_n$,表示你的马匹们的速度。保证 $1\le u_i\le 2N$。

接下来一行 $N$ 个用空格隔开的整数,依次为 $v_1,v_2,\cdots,v_n$,表示田忌的马匹们的速度。保证 $1\le v_i\le 2N$。

输出格式

输出一行,表示你最多能获胜几轮。

输入输出样例 #1

输入 #1

1
2
3
3
1 3 5
2 4 6

输出 #1

1
2

输入输出样例 #2

输入 #2

1
2
3
5
10 3 5 8 7
4 6 1 2 9

输出 #2

1
5

说明/提示

样例解释 1:

第 1 轮,田忌派出速度为 2 的马匹,你可以派出速度为 3 的马匹迎战,本轮你获胜。

第 2 轮,田忌派出速度为 4 的马匹,你可以派出速度为 5 的马匹迎战,本轮你获胜。

第 3 轮,田忌派出速度为 6 的马匹,你可以派出速度为 1 的马匹迎战,本轮田忌获胜。

如此,你可以赢得 2 轮比赛。


题目分析

题目分析

本题主要考察一维数组、排序和贪心算法的应用。

解题思路:

  1. 数据预处理
    • 读入双方马匹数量N和各自马匹速度数组
    • 对双方马匹速度数组进行排序,便于采用贪心策略
  2. 贪心策略选择
    • 方案一:从最快的马开始比较
      • 用我方最快的马去比较田忌相应位置的马
      • 如果能赢就记一分,继续比下一对
      • 如果赢不了就用更慢的马去比
      • 重复以上过程,直到比完所有马
    • 方案二:从最慢的马开始比较
      • 用我方最慢的马去比较田忌最慢的马
      • 如果能赢就记一分,双方都换下一匹马
      • 如果赢不了就换我方下一匹更快的马
  3. 获胜场次统计
    • 记录每次获胜的场次
    • 最终输出总获胜场次
  4. 时间复杂度分析:
    • 排序需要 $O(N\log N)$ 的时间复杂度
    • 方案一需要双重循环,时间复杂度为 $O(N^2)$
    • 方案二只需单重循环,时间复杂度为 $O(N)$
    • 因此方案二的总体时间复杂度为 $O(N\log N)$,优于方案一
  5. 空间复杂度分析:
    • 需要存储双方马匹速度数组,空间复杂度为 $O(N)$
    • 排序可能需要额外 $O(\log N)$ 的栈空间
    • 总体空间复杂度为 $O(N)$

本题的难点在于正确理解贪心策略,选择合适的比较方案。从最慢的马开始比较(方案二)不仅代码更简洁,时间复杂度也更优。


示例代码

方法一,用最快的去比较,双层循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <algorithm>

// 定义最大数组长度,题目要求N最大为5*10^4
const int max_n = 5 * 10e4 + 5;
// t_array存储田忌的马匹速度
int t_array[max_n];
// m_array存储我方马匹速度
int m_array[max_n];

int main() {
    // 读取输入数据
    int n;
    std::cin >> n;
    // 读入我方马匹速度
    for (int i = 0; i < n; i++) {
        std::cin >> m_array[i];
    }
    // 读入田忌马匹速度
    for (int i = 0; i < n; i++) {
        std::cin >> t_array[i];
    }

    // 对输入数据进行排序,便于后续比较
    // 将我方马匹按速度从小到大排序
    std::sort(m_array, m_array + n);
    // 将田忌马匹按速度从小到大排序
    std::sort(t_array, t_array + n);

    // 计算满足条件的数对数量
    // count记录获胜场次
    int count = 0;
    // last_j记录上一次找到的田忌马匹位置
    int last_j = n;

    // 从后向前遍历 m_array,寻找满足条件的数对
    // 采用贪心策略,用我方最快的马去比较
    for (int i = n - 1; i >= 0; i--) {
        // 从最后一个满足条件的数开始向前遍历 t_array,找到第一个满足条件的数对
        // 寻找比我方马匹慢的田忌马匹中最快的一匹
        for (int j = last_j - 1; j >= 0; j--) {
            // 如果找到一匹比田忌的马快,就可以获胜
            if (m_array[i] > t_array[j]) {
                count++;
                // 更新最后一个满足条件的数的索引,避免重复使用
                last_j = j;
                break;
            }
        }
    }

    // 输出满足条件的数对数量,即最多获胜场次
    std::cout << count;
    return 0;
}

方法二,从最慢的马开始比较,单层循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <algorithm>

// 定义最大数组长度,题目要求N最大为5*10^4
const int max_n = 5 * 10e4 + 5;
// t_array存储田忌的马匹速度
int t_array[max_n];
// m_array存储我方马匹速度
int m_array[max_n];

int main() {
    // 读取马匹数量N
    int n;
    std::cin >> n;
    
    // 读取我方马匹速度数组
    for (int i = 0; i < n; i++) {
        std::cin >> m_array[i];
    }
    
    // 读取田忌马匹速度数组
    for (int i = 0; i < n; i++) {
        std::cin >> t_array[i];
    }

    // 对两方马匹速度进行排序,便于采用贪心策略
    std::sort(m_array, m_array + n);
    std::sort(t_array, t_array + n);

    // count记录获胜场次
    int count = 0;

    // 采用贪心策略,从最慢的马开始比较
    // i遍历我方马匹,j遍历田忌马匹
    // 如果我方当前马匹比田忌当前马匹快,就可以获胜一场
    for (int i = 0, j = 0; i < n; i++) {
        // 如果我方当前马比田忌当前马快,就可以获胜
        if (m_array[i] > t_array[j]) {
            // 获胜场次加1
            count++;
            // 田忌下一匹马
            j++;
        }
        // 如果我方马不够快,就跳过这轮,用下一匹更快的马去比
    }

    // 输出最多获胜场次
    std::cout << count;
    return 0;
}

所有代码已上传至Github:https://github.com/lihongzheshuai/yummy-code

GESP各级别考纲、真题讲解、知识拓展和练习清单等详见【置顶】【GESP】C++ 认证学习资源汇总

luogu-”系列题目已加入洛谷Java、C++初学团队作业清单,可在线评测,团队名额有限。

bcqm-”系列题目可在编程启蒙题库进行在线评测。

欢迎加入Java、C++、Python技术交流QQ群(982860385),大佬免费带队,有问必答

欢迎加入C++ GESP/CSP认证学习QQ频道,考试资源总结汇总

欢迎加入C++ GESP/CSP学习交流QQ群(688906745),考试认证学员交流,互帮互助

GESP/CSP 认证学习微信公众号
GESP/CSP 认证学习微信公众号
本文由作者按照 CC BY 4.0 进行授权