题解:洛谷 B3928 田忌赛马
【题目来源】
洛谷: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\)。
【输出】
输出一行,表示你最多能获胜几轮。
【输入样例】
3
1 3 5
2 4 6
【输出样例】
2
【核心思想】
-
问题分析:给定双方各 \(N\) 匹马的速度,田忌按固定顺序派出马匹,我方可以自由安排出场顺序。求最多能获胜的轮次。这是一个贪心匹配问题,核心在于对双方马匹排序后,用双指针策略找到最优匹配方案。
-
算法选择:
- 排序 + 双指针:将双方马匹速度分别排序,用贪心策略为田忌的每匹马匹配能击败它的最慢的马
-
关键步骤:
- 读入数据:读取 \(N\),数组 \(u[1..N]\)(我方),\(v[1..N]\)(田忌)
- 排序:\(sort(u+1, u+N+1)\),\(sort(v+1, v+N+1)\)
- 双指针匹配:
- \(j \leftarrow 1\)(我方指针)
- 遍历田忌的马 \(i\) 从 \(1\) 到 \(N\):
- 跳过所有 \(u[j] < v[i]\) 的马(无法获胜)
- 若 \(j \leq N\):\(ans \leftarrow ans + 1\),\(j \leftarrow j + 1\)(用当前马击败田忌的马)
- 否则 \(break\)(无可用马匹)
- 输出结果:\(ans\)
-
时间/空间复杂度:
- 时间复杂度:\(O(N \log N)\),排序主导
- 空间复杂度:\(O(N)\),存储数组
-
贪心匹配与双指针的核心思想:
- 排序转化问题:田忌的马按出场顺序固定,但我方可以重新排列。将双方排序后,问题转化为:在有序序列中,我方每个元素最多使用一次,求能大于对方对应元素的最大匹配数
- 最慢获胜策略:对于田忌的每匹马 \(v[i]\),选择能击败它的最慢的马 \(u[j]\)。这样保留更快的马去应对更强的对手,是全局最优的贪心策略
- 双指针的单调性:双方排序后,若 \(u[j]\) 无法击败 \(v[i]\),则 \(u[1..j]\) 都无法击败 \(v[i]\),\(j\) 只需单向右移,无需回溯
- 田忌赛马的变体:经典田忌赛马是双方同时自由安排顺序,本题是田忌固定顺序、我方自由安排,简化为贪心匹配问题
- 适用于有序序列的贪心匹配、资源最优分配类问题
【算法标签】
普及- #双指针
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 50005; // 最大数组长度
int n; // 数组大小
int ans; // 答案:满足条件的配对数量
int u[N], v[N]; // 两个数组
int main()
{
// 输入数组大小
cin >> n;
// 输入并排序数组u
for (int i = 1; i <= n; i++)
{
cin >> u[i];
}
sort(u + 1, u + n + 1);
// 输入并排序数组v
for (int i = 1; i <= n; i++)
{
cin >> v[i];
}
sort(v + 1, v + n + 1);
// 双指针贪心匹配
int j = 1; // u数组的指针
// 遍历v数组
for (int i = 1; i <= n; i++)
{
// 跳过所有小于v[i]的u[j]
while (j <= n && u[j] < v[i])
{
j++;
}
// 调试输出
// cout << "j " << j << endl;
// 如果找到满足条件的u[j](即u[j] >= v[i])
if (j <= n)
{
ans++; // 成功配对数加1
j++; // 这个u[j]已被使用,指针后移
}
else
{
// 没有更多可用的u,提前结束
break;
}
}
// 输出结果
cout << ans << endl;
return 0;
}
【运行结果】
3
1 3 5
2 4 6
2
浙公网安备 33010602011771号