atcoder ABC442 题解

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ᵢ 之间存在利益冲突。

一篇论文的评审团需满足以下条件:

  1. 三名互不相同的研究人员组成;
  2. 评审团成员均不是该论文的作者;
  3. 评审团成员均与该论文作者无利益冲突

对于每个研究人员 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. 1 x:交换 Aₓ 和 Aₓ₊₁ 的值。
  2. 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 次实验的规则如下:

  1. 初始时,高桥面向怪物 Aⱼ 所在的方向;
  2. 随后高桥顺时针旋转,直到面向怪物 Bⱼ 所在的方向时停止;
  3. 求此过程中被激光摧毁的怪物总数(包含 Aⱼ 和 Bⱼ 对应的方向上的所有怪物);
  4. 若 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) 为白色;若为 #,则为黑色。

你需要重新涂色若干格子,使得以下两个条件同时满足:

  1. 每行条件:对于每一行,存在整数 k(0 ≤ k ≤ N),使得该行最左侧的 k 个格子为白色,其余格子为黑色;
  2. 每列条件:对于每一列,存在整数 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. 按重量分组并筛选最优物品 : 将所有物品按重量分为 1、2、3 三组,对每组内的物品按单位重量价值(V/W)从高到低排序。由于要最大化总价值,同一重量下优先选择单位价值更高的物品,因此每组仅需保留单位价值最高的物品(其余低价值物品无需考虑),并记录该物品可选取的最大数量(不超过自身持有数 Kᵢ,且不超过总容量 C 除以对应重量)。
  2. 利用最小公倍数简化问题: 1、2、3 的最小公倍数为 6,我们可以将「任意数量的 1/2/3 重量物品」组合转化为「重量为 6 的等效物品」来分析: - 例如:6 个重量 1 的物品(总重 6)、3 个重量 2 的物品(总重 6)、2 个重量 3 的物品(总重 6),均为「重量 6 的组合单位」; - 对每个「重量 6 的组合单位」,计算其总价值(如 6 个重量 1 的最优物品价值和、3 个重量 2 的最优物品价值和等),按总价值从高到低排序,优先选择价值更高的组合单位,快速凑出「6 的倍数重量」的最优解。
  3. 枚举余数覆盖所有情况: 总容量 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;
}
相关推荐
Aevget3 小时前
MFC扩展库BCGControlBar Pro v37.2新版亮点:控件功能进一步升级
c++·mfc·界面控件
探序基因3 小时前
单细胞Seurat数据结构修改分群信息
数据结构
六义义3 小时前
java基础十二
java·数据结构·算法
四维碎片3 小时前
QSettings + INI 笔记
笔记·qt·算法
Tansmjs3 小时前
C++与GPU计算(CUDA)
开发语言·c++·算法
独自破碎E4 小时前
【优先级队列】主持人调度(二)
算法
weixin_445476684 小时前
leetCode每日一题——边反转的最小成本
算法·leetcode·职场和发展
打工的小王4 小时前
LeetCode Hot100(一)二分查找
算法·leetcode·职场和发展
Swift社区4 小时前
LeetCode 385 迷你语法分析器
算法·leetcode·职场和发展
sonadorje5 小时前
svd在图像处理中的应用
算法