题解:洛谷 AT_abc460_c Sushi
【题目来源】
洛谷:AT_abc460_c [ABC460C] Sushi - 洛谷
【题目描述】
There are \(N\) pieces of shari (vinegared rice) and \(M\) pieces of neta (toppings) as ingredients for sushi.
The weight of the \(i\)-th shari is \(A_i\), and the weight of the \(j\)-th neta is \(B_j\).
You will make sushi by combining shari and neta.
To make one piece of sushi, you need to combine one shari with one neta. Here, the weight of the neta must be at most twice the weight of the shari. Also, the same shari or neta cannot be used in multiple pieces of sushi.
Find the maximum number of sushi that can be made.
有 \(N\) 块舍利(醋饭)和 \(M\) 块寿司配料(配料)作为寿司的食材。
第 \(i\) 块舍利的重量为 \(A_i\),第 \(j\) 块配料的重量为 \(B_j\)。
你需要通过组合舍利和配料来制作寿司。
制作一个寿司需要一块舍利和一块配料。这里,配料的重量必须不超过舍利重量的两倍。同时,同一块舍利或配料不能用于制作多个寿司。
求最多能制作的寿司数量。
【输入】
The input is given from Standard Input in the following format:
\(N\) \(M\)
\(A_1\) \(A_2\) \(\ldots\) \(A_N\)
\(B_1\) \(B_2\) \(\ldots\) \(B_M\)
【输出】
Output the answer.
【输入样例】
4 5
4 2 1 8
14 9 3 2 9
【输出样例】
3
【核心思想】
-
问题分析:给定 \(N\) 块舍利(醋饭)的重量 \(A_i\) 和 \(M\) 块配料的重量 \(B_j\)。制作一个寿司需要将一块舍利和一块配料配对,要求配料重量不超过舍利重量的两倍(\(B_j \leq 2 \times A_i\))。每块舍利和配料最多使用一次。目标是最大化能制作的寿司数量。这是一个双指针贪心问题,关键在于如何最优地配对舍利和配料。
-
算法选择:
- 排序 + 双指针贪心:先将舍利重量和配料重量分别升序排序,然后用双指针进行贪心匹配
- 贪心策略:优先用重量较小的舍利去匹配能配对的最小配料,保留重量较大的舍利给重量更大的配料
-
关键步骤:
- 排序:将数组 \(A[1..N]\)(舍利重量)和 \(B[1..M]\)(配料重量)分别升序排序
- 初始化:指针 \(i = 1\)(指向当前舍利),指针 \(j = 1\)(指向当前配料),计数器 \(ans = 0\)
- 双指针遍历(当 \(i \leq N\) 且 \(j \leq M\) 时):
- 检查匹配条件:如果 \(B[j] \leq 2 \times A[i]\)(当前配料能与当前舍利配对)
- 匹配成功:\(i++\),\(j++\),\(ans++\)
- 无法匹配:如果 \(B[j] > 2 \times A[i]\)(当前舍利重量太小)
- \(i++\)(尝试下一个重量更大的舍利)
- 检查匹配条件:如果 \(B[j] \leq 2 \times A[i]\)(当前配料能与当前舍利配对)
- 输出最大寿司数量 \(ans\)
-
时间/空间复杂度:
- 时间复杂度:\(O(N \log N + M \log M)\),主要是排序的复杂度
- 空间复杂度:\(O(N + M)\),存储舍利和配料信息
-
双指针贪心的核心思想:
- 排序预处理:将两个序列排序后,问题转化为在有序序列中寻找最优匹配
- 贪心正确性:对于每个配料,使用能满足条件的最小舍利,这样可以保留更大的舍利给更重的配料
- 单调性:由于两个序列都升序排列,如果当前舍利无法与当前配料配对,那么重量更小的舍利也无法配对
- 最优性证明:这种贪心策略能得到最大匹配数,因为每个配料都使用了能满足条件的重量最小的舍利,不会浪费大舍利
- 适用于带约束条件的两类物品匹配问题
【算法标签】
普及- #双指针
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200005;
int n, m, ans; // n: 第一个数组长度,m: 第二个数组长度,ans: 匹配数量
int a[N], b[N]; // 两个数组
signed main()
{
cin >> n >> m; // 输入两个数组的长度
for (int i = 1; i <= n; i++) // 输入第一个数组
cin >> a[i];
for (int i = 1; i <= m; i++) // 输入第二个数组
cin >> b[i];
sort(a + 1, a + n + 1); // 排序第一个数组
sort(b + 1, b + m + 1); // 排序第二个数组
int i = 1, j = 1; // 双指针
while (i <= n && j <= m) // 当两个指针都在数组范围内
{
if (b[j] <= 2 * a[i]) // 如果满足条件
{
i++; // 移动第一个数组的指针
j++; // 移动第二个数组的指针
ans++; // 匹配数量加1
}
else // 如果不满足条件
{
i++; // 移动第一个数组的指针
}
}
cout << ans << endl; // 输出匹配数量
return 0;
}
【运行结果】
4 5
4 2 1 8
14 9 3 2 9
3
浙公网安备 33010602011771号