atcoder ABC#442 题解
A - 计数点号
题目描述
给你一个由小写英文字母组成的字符串 S。
在此规定,每个小写英文字母对应的 "点号数量" 定义如下:
- 若该小写字母是 i 或 j:对应 1 个点号
- 若该小写字母既不是 i 也不是 j:对应 0 个点号
请计算字符串 S 中所有字符的点号数量之和。
约束条件
- S 是由小写英文字母组成的字符串,长度在 1 到 10 之间(包含 1 和 10)。
输入格式
输入从标准输入按以下格式给出:
blank
S
输出格式
输出答案。
解题思路
枚举整个字符串,没遇到一个 i 或者 j 就让计数器加一,最后输出计数器的值。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
string s;
int main()
{
cin >> s;
int ans = 0;
for (int i = 0; i < s.size(); i++)
if (s[i] == 'i' || s[i] == 'j')
ans++;
cout << ans;
return 0;
}
B - 音乐播放器
题目描述
高桥有一个音乐播放器。初始状态下,音量为 0,音乐处于停止状态。
接下来会按顺序执行 Q 次操作。第 i 次操作由整数 Aᵢ 表示,具体含义如下:
- 若 Aᵢ = 1:将音量增加 1。
- 若 Aᵢ = 2:如果当前音量大于等于 1,则将音量减少 1;如果当前音量为 0,则不执行任何操作。
- 若 Aᵢ = 3:如果音乐处于停止状态,则开始播放;如果音乐正在播放,则停止播放。
对于 i = 1, 2, ..., Q,需要解决以下问题:
- 判断执行完第 i 次操作后,音乐是否处于播放状态且音量大于等于 3。
约束条件
- 1 ≤ Q ≤ 2×10⁵
- Aᵢ ∈ {1, 2, 3}
- 所有输入值均为整数。
输入格式
输入从标准输入按以下格式给出:
blank
Q
A1
A2
...
AQ
输出格式
输出 Q 行。第 i 行需满足:若执行完第 i 次操作后音乐处于播放状态且音量 ≥3≥3≥3,则输出 Yes,否则输出 No。
解题思路
模拟题,需要维护两个值:当前音乐播放器的状态、音量。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
int q, onplay, v;
int main()
{
cin >> q;
while (q--)
{
int a;
cin >> a;
if (a == 1)
v++;
if (a == 2)
v = max(0, v - 1);
if (a == 3)
onplay ^= 1;
if (v >= 3 && onplay)
cout << "Yes\n";
else
cout << "No\n";
}
return 0;
}
C - 同行评审
题目描述
有 N 名研究人员,编号为 1, 2, ..., N。
研究人员之间存在 M 对利益冲突关系:对于第 i 对关系 (1 ≤ i ≤ M),研究人员 Aᵢ 和 Bᵢ 之间存在利益冲突。
一篇论文的评审团需满足以下条件:
- 由三名互不相同的研究人员组成;
- 评审团成员均不是该论文的作者;
- 评审团成员均与该论文作者无利益冲突。
对于每个研究人员 i (1 ≤ i ≤ 1, 2, ..., N),请解决以下问题:
计算以研究人员 i 为论文作者时,可能的评审团三元组的数量。
注:所有论文均为独著(即每位作者仅自己撰写论文)。
约束条件
- 1 ≤ N ≤ 2×10⁵
- 0 ≤ M ≤ 2×10⁵
- 1 ≤ Aᵢ, Bᵢ ≤ N
- Aᵢ ≠ Bᵢ
- 若 i ≠ j,则 (Aᵢ, Bᵢ) ≠ (Aⱼ, Bⱼ)
- 若 i ≠ j,则 (Aᵢ, Bᵢ) ≠ (Bⱼ, Aⱼ)
- 所有输入值均为整数。
输入格式
输入从标准输入按以下格式给出:
N M
A₁ B₁
A₂ B₂
⋮
A_M B_M
输出格式
按顺序输出研究人员 1 到 N 对应的答案,用空格分隔。
解题思路
记录每个人与其余多少个人有利益冲突,然后计算有多少个人可以当评委,然后用组合数计算方案数。
代码
cpp
#include <bits/stdc++.h>
#define int long long // 将int类型重定义为long long,避免溢出(组合数计算会产生大数)
using namespace std; // 使用std命名空间,简化代码书写
const int maxn = 2e5 + 5;
int n, m; // n:研究人员总数;m:利益冲突关系的数量
int d[maxn]; // d[i]:存储第i个研究人员的利益冲突人数
signed main()
{
// 第一步:读取输入的研究人员数n和冲突关系数m
cin >> n >> m;
// 第二步:统计每个研究人员的冲突人数
for (int i = 1; i <= m; i++)
{
int a, b; // 存储一组冲突关系的两个研究人员编号
cin >> a >> b;
d[a]++; // 研究人员a的冲突人数+1
d[b]++; // 研究人员b的冲突人数+1
}
// 第三步:计算每个研究人员作为作者时的评审三元组数量
for (int i = 1; i <= n; i++)
{
// 核心公式:计算可作为评审的人数
// num = 总人数n - 作者自己(1人) - 与作者i有冲突的人数d[i]
int num = n - d[i] - 1;
// 计算组合数C(num, 3) = num*(num-1)*(num-2)/6
// 若num < 3,乘法结果会是负数/0,除以6后自然为0(符合题目要求)
cout << num * (num - 1) * (num - 2) / 6 << ' ';
}
return 0;
}
D - 交换与区间求和
题目描述
给你一个长度为 N 的序列 A = (A₁, A₂, ..., A_N)。
按顺序处理 Q 个查询,每个查询为以下两种格式之一:
1 x:交换 Aₓ 和 Aₓ₊₁ 的值。2 l r:计算区间 [l, r] 内所有元素的和(即 ∑(i=l 到 r) Aᵢ)。
约束条件
- 2 ≤ N ≤ 2×10⁵
- 1 ≤ Q ≤ 5×10⁵
- 1 ≤ Aᵢ ≤ 10⁴
- 对于类型 1 的查询:1 ≤ x ≤ N−1
- 对于类型 2 的查询:1 ≤ l ≤ r ≤ N
- 所有输入值均为整数。
输入格式
输入从标准输入按以下格式给出:
N Q
A₁ A₂ ... A_N
query₁
query₂
⋮
query_Q
其中 queryᵢ 表示第 i 个查询,格式为:
- 类型 1:
1 x - 类型 2:
2 l r
输出格式
设类型 2 的查询数量为 q,请输出 q 行。第 i 行 (1 ≤ i ≤ q) 输出第 i 个类型 2 查询的答案。
解题思路
我们维护一个前缀和,每次交换相邻的两个元素也只会对这两个前缀和的值产生影响,我们简单修改即可。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int n, q; // n:序列长度;q:查询次数
int sum[maxn]; // sum[i]:前缀和数组,sum[i] = A₁+A₂+...+Aᵢ
int a[maxn]; // 存储原始序列
int main()
{
cin >> n >> q;
// 初始化原始数组和前缀和数组
for (int i = 1; i <= n; i++)
cin >> a[i], sum[i] = sum[i - 1] + a[i];
while (q--)
{
int type;
cin >> type;
// 类型1:交换A[x]和A[x+1]
if (type == 1)
{
int x;
cin >> x;
// 错误逻辑:仅更新sum[x]和sum[x+1],未更新x+1之后的前缀和
sum[x] += a[x + 1] - a[x]; // 等价于sum[x] = sum[x-1] + a[x+1]
sum[x + 1] = sum[x] + a[x]; // 等价于sum[x+1] = sum[x](新) + a[x](原)
swap(a[x], a[x + 1]); // 交换原始数组的两个元素
}
// 类型2:查询区间[l,r]的和,用前缀和公式计算
else
{
int l, r;
cin >> l >> r;
cout << sum[r] - sum[l - 1] << endl;
}
}
return 0;
}
E - 激光高桥
题目描述
二维平面上有 N 只怪物,编号为 1 到 N。第 i 只怪物的坐标为 (Xᵢ, Yᵢ),且满足 (Xᵢ, Yᵢ) ≠ (0, 0)(每只怪物可视为无大小的静止点)。
高桥站在平面原点,他的双眼会发射一道强力激光:激光会瞬间摧毁他所面向方向上的所有怪物;若同一方向上有多个怪物,所有怪物都会被瞬间摧毁。
青木进行了 Q 次独立的思想实验,第 j 次实验的规则如下:
- 初始时,高桥面向怪物 Aⱼ 所在的方向;
- 随后高桥顺时针旋转,直到面向怪物 Bⱼ 所在的方向时停止;
- 求此过程中被激光摧毁的怪物总数(包含 Aⱼ 和 Bⱼ 对应的方向上的所有怪物);
- 若 Aⱼ 和 Bⱼ 位于原点的同一方向,则高桥不旋转,仅统计该方向的怪物数量。
请回答每次思想实验的答案。
约束条件
- 2 ≤ N ≤ 2×10⁵
- 1 ≤ Q ≤ 2×10⁵
- −10⁹ ≤ Xᵢ, Yᵢ ≤ 10⁹
- (Xᵢ, Yᵢ) ≠ (0, 0)
- 1 ≤ Aⱼ, Bⱼ ≤ N 且 Aⱼ ≠ Bⱼ
- 所有输入值均为整数。
输入格式
输入从标准输入按以下格式给出:
N Q
X₁ Y₁
X₂ Y₂
⋮
X_N Y_N
A₁ B₁
A₂ B₂
⋮
A_Q B_Q
输出格式
输出 Q 行,第 j 行输出第 j 次思想实验的答案。
解题思路
先把每一个坐标进行预处理,除以他们的 gcd,这样的话就能使得相同方向的坐标也是一样的。
接下来按照角度进行排序(使用 atan2 精度不够),这里推荐使用叉积的方式来判断两个向量之间的相对关系。
假设这里有两个向量 a⃗,b⃗\vec{a},\vec{b}a ,b ,如果 b⃗\vec{b}b 在 a⃗\vec{a}a 的逆时针方向,则二者的叉积 >0>0>0;反之则小于 <0<0<0。
排序之后我们将整个序列进行复制,这样的话方便我们进行环形的判断。最后用前缀和来进行求解。
代码
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 4e5 + 5; // 开2倍空间适配环形前缀和
int n, q; // n:怪物数;q:查询数
int pos[maxn]; // pos[i]:第i个原始怪物对应的「标准化方向」的索引
int sum[maxn]; // 前缀和数组(环形展开),sum[i]表示前i个方向的怪物总数
// 存储怪物的坐标+辅助信息
struct point
{
int x, y; // 标准化后的坐标(方向)
int num; // 该方向上的怪物数量(去重后计数)
int id; // 原始怪物编号(用于映射pos数组)
} a[maxn];
// 计算最大公约数(用于方向标准化)
int gcd(int x, int y)
{
if (x == 0) return y;
if (y == 0) return x;
return gcd(y % x, x);
}
// 极角排序规则:按顺时针方向排序所有方向
bool cmp(point a, point b)
{
// 第一步:将方向分为「上半平面」(y>0 或 y=0且x>0)和「下半平面」(y<0 或 y=0且x<0)
bool ha = (a.y < 0) || (a.y == 0 && a.x < 0); // a是否在下半平面
bool hb = (b.y < 0) || (b.y == 0 && b.x < 0); // b是否在下半平面
if (ha != hb) return ha < hb; // 上半平面的方向排在下半平面前面(顺时针优先)
// 第二步:同一半平面内,用叉积判断顺时针顺序(cross<0 表示a在b的顺时针方向)
int cross = a.x * b.y - a.y * b.x;
if (cross != 0) return cross < 0;
// 第三步:叉积为0(同一方向),无需排序
return false;
}
// 判断两个点是否为「同一方向」(标准化后坐标相同)
bool equal(point a, point b)
{
return (a.x == b.x && a.y == b.y);
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> q;
for (int i = 1; i <= n; i++)
{
cin >> a[i].x >> a[i].y;
// 步骤1:方向标准化------约去x/y的最大公约数,统一方向表示
int g = gcd(abs(a[i].x), abs(a[i].y));
a[i].x /= g;
a[i].y /= g;
a[i].id = i; // 记录原始编号,用于后续映射pos数组
}
// 步骤2:按顺时针极角排序所有怪物的方向
sort(a + 1, a + n + 1, cmp);
// 步骤3:去重,统计每个唯一方向的怪物数量
int m = 0; // m:去重后的唯一方向数
for (int i = 1; i <= n; i++)
{
// 新方向(第一个/与上一个方向不同)
if (i == 1 || !equal(a[i], a[m]))
a[++m] = a[i];
a[m].num++; // 该方向的怪物数+1
pos[a[i].id] = m; // 记录原始怪物i对应的方向索引
}
// 步骤4:构建环形前缀和(展开为2*m长度,处理「跨半平面」的顺时针区间)
for (int i = 0; i < 2 * m; i++)
// i%m+1 实现环形遍历:1→m→1→m...
sum[i + 1] = sum[i] + a[i % m + 1].num;
// 处理每个查询
while (q--)
{
int l, r; // 输入的A_j和B_j(原始怪物编号)
cin >> l >> r;
// 映射为方向索引
l = pos[l];
r = pos[r];
// 处理环形区间:若l>r(顺时针旋转跨下半平面),则r += m(展开为线性区间)
if (l > r) r += m;
// 前缀和求区间和:[l, r] 内的怪物总数
cout << sum[r] - sum[l - 1] << endl;
}
return 0;
}
F - 对角线分隔 2
题目描述
有一个 N 行 N 列的网格,从上到下第 i 行、从左到右第 j 列的格子记为 (i,j)。
网格中的每个格子被涂成白色(.)或黑色(#)。网格的颜色信息由 N 个字符串 S₁, S₂, ..., S_N 给出:若 S_i 的第 j 个字符是 .,则格子 (i,j) 为白色;若为 #,则为黑色。
你需要重新涂色若干格子,使得以下两个条件同时满足:
- 每行条件:对于每一行,存在整数 k(0 ≤ k ≤ N),使得该行最左侧的 k 个格子为白色,其余格子为黑色;
- 每列条件:对于每一列,存在整数 k(0 ≤ k ≤ N),使得该列最上方的 k 个格子为白色,其余格子为黑色。
请找出满足条件所需的最少重涂色格子数。
约束条件
- 1 ≤ N ≤ 5000
- N 为整数
- S_i 是长度为 N 的字符串,仅包含
.和#。
输入格式
输入从标准输入按以下格式给出:
N
S₁
S₂
⋮
S_N
输出格式
输出满足条件的最少重涂色格子数。
解题思路
简单 dp 题,感觉比 E 要简单。
dp[i][j]dp[i][j]dp[i][j] 表示第 iii 行前 jjj 个元素全部为白色,右边全部为黑色,如果要使得前 iii 行全部合法最少需要涂多少个格子。
dp[i][j]=min(dp[i−1][k])+第i层涂的方格数,k∈[j,n]dp[i][j] = min(dp[i - 1][k]) + 第 i 层涂的方格数,k \in [j, n]dp[i][j]=min(dp[i−1][k])+第i层涂的方格数,k∈[j,n]
因为 min 里面的内容是每一行的后缀,所以我们在进行状态转移的时候可以从达到小枚举 j,每一次更新后缀最小值即可。
对于每一层需要涂的方格数,我们可以使用一个前缀和、一个后缀和来计算白色格子的数量(黑色格子数量就是总数减去白色数量)。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5005;
int n;
// a[i][j]:标记格子(i,j)的原始颜色,1=白色(.),0=黑色(#)
int a[maxn][maxn];
// dp[i][j]:处理前i行,且第i行的白色分界点为j(前j格白、j+1到n格黑)时的最小重涂数
int dp[maxn][maxn];
// l[i][j]:第i行前j格的白色数量(前缀和);r[i][j]:第i行从j到n格的白色数量(后缀和)
int l[maxn][maxn], r[maxn][maxn];
int main()
{
cin >> n;
// 步骤1:读取网格颜色,初始化a数组
for (int i = 1; i <= n; i++)
{
string s;
cin >> s;
for (int j = 1; j <= n; j++)
// a[i][j]=1 表示原始是白色,0表示原始是黑色
a[i][j] = (int)(s[j - 1] == '.');
}
// 步骤2:预处理每行的前缀/后缀白色数量(用于快速计算重涂代价)
for (int i = 1; i <= n; i++)
{
// l[i][j]:第i行前j格的白色总数(前缀和)
for (int j = 1; j <= n; j++)
l[i][j] = l[i][j - 1] + a[i][j];
// r[i][j]:第i行从j到n格的白色总数(后缀和)
for (int j = n; j >= 1; j--)
r[i][j] = r[i][j + 1] + a[i][j];
}
// 步骤3:动态规划计算最小重涂数
for (int i = 1; i <= n; i++) // 逐行处理
{
int min_op = 0x3f3f3f3f; // 记录前i-1行的最小dp值(优化DP转移)
// 倒序枚举第i行的分界点j(0表示全黑,n表示全白)
for (int j = n; j >= 0; j--)
{
// 转移:前i-1行的最小代价 + 第i行以j为分界的重涂代价
min_op = min(min_op, dp[i - 1][j]);
// 第i行重涂代价拆解:
// j - l[i][j]:前j格需要涂成白色的数量(j格应全白,减去原本白色数=需要涂黑→涂白的数量)
// r[i][j+1]:j+1到n格需要涂成黑色的数量(原本白色数=需要涂黑的数量)
dp[i][j] = min_op + j - l[i][j] + r[i][j + 1];
}
}
// 步骤4:找所有可能分界点的最小重涂数(处理完n行后的所有j)
int ans = 0x3f3f3f3f;
for (int i = 0; i <= n; i++)
ans = min(ans, dp[n][i]);
cout << ans << endl;
return 0;
}
G - 轻量级背包
题目描述
有 N 种物品,第 i 种物品的重量为 Wᵢ、价值为 Vᵢ,且你拥有 Kᵢ 个该物品。
请从所有物品(总数为 K₁+K₂+...+K_N)中选择若干个(可以选 0 个),使得所选物品的总重量不超过 C,求能获得的最大总价值。
约束条件
- 1 ≤ N ≤ 2×10⁵
- 1 ≤ C ≤ 2×10⁹
- 1 ≤ Wᵢ ≤ 3(关键约束:物品重量仅为 1、2、3)
- 1 ≤ Vᵢ ≤ 10⁹
- 1 ≤ Kᵢ ≤ 10⁹
- 所有输入值均为整数。
输入格式
输入从标准输入按以下格式给出:
N C
W₁ V₁ K₁
W₂ V₂ K₂
⋮
W_N V_N K_N
输出格式
输出能获得的最大总价值。
解题思路
由于物品的重量仅包含 1、2、3 三种(核心约束),我们可以围绕这一特性做针对性优化:
- 按重量分组并筛选最优物品 : 将所有物品按重量分为 1、2、3 三组,对每组内的物品按单位重量价值(V/W)从高到低排序。由于要最大化总价值,同一重量下优先选择单位价值更高的物品,因此每组仅需保留单位价值最高的物品(其余低价值物品无需考虑),并记录该物品可选取的最大数量(不超过自身持有数 Kᵢ,且不超过总容量 C 除以对应重量)。
- 利用最小公倍数简化问题: 1、2、3 的最小公倍数为 6,我们可以将「任意数量的 1/2/3 重量物品」组合转化为「重量为 6 的等效物品」来分析: - 例如:6 个重量 1 的物品(总重 6)、3 个重量 2 的物品(总重 6)、2 个重量 3 的物品(总重 6),均为「重量 6 的组合单位」; - 对每个「重量 6 的组合单位」,计算其总价值(如 6 个重量 1 的最优物品价值和、3 个重量 2 的最优物品价值和等),按总价值从高到低排序,优先选择价值更高的组合单位,快速凑出「6 的倍数重量」的最优解。
- 枚举余数覆盖所有情况: 总容量 C 未必是 6 的整数倍,因此在凑完所有「6 的倍数重量」后,剩余重量(余数)范围为 0~5。我们只需枚举这 6 种余数情况(0、1、2、3、4、5),对每种余数,再枚举少量 1/2/3 重量的物品数量(最多枚举 6 次),计算剩余重量能填充的最大价值,最终结合「6 的倍数部分」的最优解得到全局最大值。
代码
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
/**
* @brief 核心辅助函数:选取指定数量的物品,并将剩余物品打包为「固定重量的组合单位」
* @param items 按单位价值升序排列的物品列表(pair<价值, 数量>)
* @param take_num 需要优先选取的物品数量(用于覆盖余数)
* @param group_size 组合单位的大小(如W=1的物品按6个为1组,W=2按3个为1组)
* @return pair<优先选取的总价值, 剩余物品打包后的组合列表>
* 组合列表中每个元素为<单组价值, 组数>(按单位价值升序)
*/
pair<int, vector<pair<int, int>>> merge(vector<pair<int, int>> items, int take_num, int group_size)
{
int top_sum = 0; // 优先选取take_num个物品的总价值
// 第一步:选取前take_num个高价值物品(items已升序,取末尾=高价值)
while (take_num > 0 && !items.empty())
{
int value = items.back().first; // 当前物品的单位价值
int count = items.back().second; // 当前物品的剩余数量
if (count > take_num)
{
top_sum += value * take_num; // 数量足够,取take_num个
items.back().second -= take_num;
break;
}
top_sum += value * count; // 数量不足,全取
take_num -= count;
items.pop_back();
}
// 第二步:将剩余物品打包为「group_size个为1组」的组合单位
vector<pair<int, int>> result;
int current_cnt = 0, current_sum = 0; // 当前未凑满1组的数量/价值
while (!items.empty())
{
int value = items.back().first;
int count = items.back().second;
items.pop_back();
// 先凑满当前未完成的组
if (current_cnt > 0)
{
if (current_cnt + count < group_size)
{
current_cnt += count;
current_sum += value * count;
continue;
}
// 凑满1组,加入结果
int need = group_size - current_cnt;
current_sum += value * need;
result.push_back({current_sum, 1});
count -= need;
current_cnt = 0;
current_sum = 0;
}
// 处理剩余数量,按group_size打包
if (count >= group_size)
{
// 整组打包:单组价值=单位价值*组大小,组数=总数量/组大小
result.push_back({value * group_size, count / group_size});
}
// 剩余不足1组的部分,暂存
current_cnt = count % group_size;
current_sum = value * current_cnt;
}
return {top_sum, result};
}
signed main()
{
ios::sync_with_stdio(false); // 加速输入输出
cin.tie(nullptr);
int n, c; // n=物品种类数,c=总容量
cin >> n >> c;
// 按重量分组存储物品:pair<单位价值, 数量>
vector<pair<int, int>> items1, items2, items3;
for (int i = 0; i < n; i++)
{
int w, v, k;
cin >> w >> v >> k;
if (w == 1) items1.push_back({v, k}); // W=1:单位价值=v
else if (w == 2) items2.push_back({v, k});// W=2:单位价值=v
else items3.push_back({v, k}); // W=3:单位价值=v
}
// 升序排序:保证后续取末尾为高价值物品
sort(items1.begin(), items1.end());
sort(items2.begin(), items2.end());
sort(items3.begin(), items3.end());
int ans = 0; // 最终最大价值
// 枚举余数组合:覆盖所有非6倍数的重量(0~5)
// p=W=1的数量(0~5),q=W=2的数量(0~2),r=W=3的数量(0~1),总重量=p+2q+3r ≤5
for (int p = 0; p < 6; p++)
for (int q = 0; q < 3; q++)
for (int r = 0; r < 2; r++)
{
// 复制物品列表(避免修改原数据)
auto list1 = items1;
auto list2 = items2;
auto list3 = items3;
// 第一步:选取余数部分的物品(覆盖非6倍数重量)
// W=1:选p个,剩余按6个为1组打包;W=2:选q个,剩余按3个为1组;W=3:选r个,剩余按2个为1组
auto [val1, g1] = merge(list1, p, 6);
auto [val2, g2] = merge(list2, q, 3);
auto [val3, g3] = merge(list3, r, 2);
// 检查余数部分的重量是否超过总容量
int used = p + q * 2 + r * 3;
if (used > c) continue;
// 第二步:计算剩余容量(6的倍数),分配给打包后的组合单位
int rem = (c - used) / 6; // 剩余容量可容纳的6重量单位数
int current = val1 + val2 + val3; // 余数部分的总价值
// 合并所有组合单位,按价值升序排序(取末尾=高价值)
vector<pair<int, int>> all_groups;
for (auto x : g1) all_groups.push_back(x);
for (auto x : g2) all_groups.push_back(x);
for (auto x : g3) all_groups.push_back(x);
sort(all_groups.begin(), all_groups.end());
// 优先选取高价值的组合单位,填充剩余容量
while (!all_groups.empty() && rem > 0)
{
int group_val = all_groups.back().first; // 单组价值(6重量单位)
int group_cnt = all_groups.back().second; // 组数
all_groups.pop_back();
if (group_cnt >= rem)
{
current += group_val * rem;
break;
}
current += group_val * group_cnt;
rem -= group_cnt;
}
// 更新最大价值
ans = max(ans, current);
}
cout << ans << endl;
return 0;
}