1 条题解

  • 0
    @ 2025-11-18 8:44:08

    「Waste Recycling」题解

    题目核心分析

    问题本质

    在三天内使用最多三种设置(每天一种)处理尽可能多的货车,需满足:

    1. 货车按原顺序处理,不可丢弃
    2. 无法处理的货车可暂存辅助轨道(仅能后进先出,且第三天结束时需为空)
    3. 优先最大化处理货车数量,若全处理则优先使用更少天数(1天>2天>3天)

    关键洞察

    辅助轨道的“后进先出”特性,等价于处理序列是原货车序列的一个子序列(可跳过部分货车暂存辅助轨道,但最终需全部处理辅助轨道货车,即子序列需覆盖所有处理的货车)。因此问题转化为:选择1-3个设置,找到最长的子序列,使其能被这三个设置按顺序处理(每个设置处理阶段可处理子序列中对应类型的货车),且第三天结束时无剩余辅助轨道货车(即子序列覆盖所有处理的货车)。

    算法设计

    核心思路

    采用“枚举+贪心”策略,分三种情况枚举(1个设置、2个设置、3个设置),分别计算每种情况下的最大处理量,最终选择最优解。

    1. 预处理:记录每个设置可处理的废物类型,以及每种废物类型对应的所有设置(优化枚举效率)。
    2. 单设置情况:直接贪心处理,直到遇到无法处理的货车。
    3. 双设置情况:先使用第一个设置处理,遇到第一个无法处理的货车时,切换到第二个设置,继续贪心处理。
    4. 三设置情况:在前两个设置处理的基础上,遇到第二个无法处理的货车时,切换到第三个设置,最终确保辅助轨道为空(通过贪心覆盖所有可处理的货车)。

    时间复杂度

    • 预处理:O(S*K + N),S为设置数,K为废物类型数,N为货车数。
    • 单设置枚举:O(S*N)。
    • 双设置枚举:O(SK1),K1为每种废物类型对应的设置数(最多10),实际为O(S10*N)。
    • 三设置枚举:O(S1010*N)。
    • 整体复杂度:O(S*N)(因10为常数),可高效处理N=2e4、S=1e3的数据。

    代码实现(可直接复制)

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <cassert>
    #include <algorithm>
    #include <string>
    #include <vector>
    #include <deque>
    #include <queue>
    #include <map>
    #include <set>
    
    using namespace std;
    
    #define eprintf(...) fprintf(stderr, __VA_ARGS__)
    #define pb push_back
    #define mp make_pair
    #define sz(x) ((int)(x).size())
    
    typedef long long ll;
    typedef vector<ll> vll;
    typedef vector<int> vi;
    typedef vector<vi> vvi;
    typedef vector<bool> vb;
    typedef vector<vb> vvb;
    typedef pair<int, int> pii;
    
    struct Ans {
        int ans;
        int a, b, c;
    
        Ans(int ans = 0, int a = -1, int b = -1, int c = -1) : ans(ans), a(a), b(b), c(c) {}
        int cnt() const {
            return (a >= 0) + (b >= 0) + (c >= 0);
        }
        bool operator<(const Ans &a2) const {
            if (ans != a2.ans)
                return ans < a2.ans;
    
            if (cnt() != a2.cnt())
                return cnt() > a2.cnt();
    
            if (a != a2.a)
                return a > a2.a;
    
            if (b != a2.b)
                return b > a2.b;
    
            return c > a2.c;
        }
    };
    
    int main() {
    #ifdef DEBUG
        freopen("std.in", "r", stdin);
        freopen("std.out", "w", stdout);
    #endif
    
        int n, k, s;
    
        while (scanf("%d%d%d", &n, &k, &s) >= 3) {
            vvb good(s, vb(k, false));
            vvi ids(k);
    
            for (int i = 0; i < s; i++) {
                for (;;) {
                    int x;
                    scanf("%d", &x);
    
                    if (!x)
                        break;
    
                    x--;
                    good[i][x] = true;
                    ids[x].pb(i);
                }
            }
    
            vi ord(n);
    
            for (int i = 0; i < n; i++)
                scanf("%d", &ord[i]), ord[i]--;
    
            Ans ans;
            vi firbad(s);
    
            // 计算单个设置的最大处理量
            for (int s1 = 0; s1 < s; s1++) {
                firbad[s1] = 0;
                while (firbad[s1] < n && good[s1][ord[firbad[s1]]])
                    firbad[s1]++;
            }
    
            // 单设置情况更新答案
            for (int s1 = 0; s1 < s; s1++)
                ans = max(ans, Ans(firbad[s1], s1));
    
            vvi firbad2(s);
    
            // 双设置情况处理
            for (int s1 = 0; s1 < s; s1++) {
                int b = firbad[s1];
                if (b >= n)
                    continue;
                b = ord[b]; // 第一个无法被s1处理的废物类型
    
                firbad2[s1] = vi(sz(ids[b]), 0);
                for (int i = 0; i < sz(ids[b]); i++) {
                    int s2 = ids[b][i]; // 枚举能处理该废物类型的s2
                    // 贪心处理s1和s2可处理的所有货车
                    for (int i2 = 0; i2 < n; i2++) {
                        if (good[s1][ord[i2]] || good[s2][ord[i2]]) {
                            ans = max(ans, Ans(i2 + 1, s1, s2));
                            firbad2[s1][i] = i2 + 1;
                        } else
                            break;
                    }
                }
            }
    
            // 三设置情况处理
            for (int s1 = 0; s1 < s; s1++) {
                int b = firbad[s1];
                if (b >= n)
                    continue;
                b = ord[b]; // s1第一个无法处理的类型
    
                // 第一种三设置组合:s1 -> s2 -> s3
                for (int i = 0; i < sz(ids[b]); i++) {
                    int s3 = ids[b][i];
                    int c = firbad2[s1][i];
                    if (c >= n)
                        continue;
                    c = ord[c]; // s1和s2组合下第一个无法处理的类型
                    for (int i2 = 0; i2 < sz(ids[c]); i2++) {
                        int s2 = ids[c][i2];
                        int cpos = firbad2[s1][i];
                        // 先处理s1和s2可处理的货车
                        while (cpos < n && (good[s1][ord[cpos]] || good[s2][ord[cpos]]))
                            cpos++;
                        // 再处理s2和s3可处理的货车(确保辅助轨道为空)
                        while (cpos < n && (good[s2][ord[cpos]] || good[s3][ord[cpos]]))
                            cpos++;
                        ans = max(ans, Ans(cpos, s1, s2, s3));
                    }
                }
    
                // 第二种三设置组合:s1 -> s2 -> s3(顺序优化)
                for (int i = 0; i < sz(ids[b]); i++) {
                    int s2 = ids[b][i];
                    int c = firbad2[s1][i];
                    if (c >= n)
                        continue;
                    c = ord[c]; // s1和s2组合下第一个无法处理的类型
                    for (int i2 = 0; i2 < sz(ids[c]); i2++) {
                        int s3 = ids[c][i2];
                        int cpos = firbad2[s1][i];
                        // 处理s2和s3可处理的货车
                        while (cpos < n && (good[s2][ord[cpos]] || good[s3][ord[cpos]]))
                            cpos++;
                        ans = max(ans, Ans(cpos, s1, s2, s3));
                    }
                }
            }
    
            // 输出结果(设置编号+1恢复原输入格式)
            printf("%d\n%d %d %d\n", ans.ans, ans.a + 1, ans.b + 1, ans.c + 1);
        }
    
        return 0;
    }
    

    代码关键模块解释

    1. 数据结构定义

    • good[s][k]:布尔矩阵,标记设置s是否能处理废物类型k
    • ids[k]:向量数组,存储所有能处理废物类型k的设置编号(优化枚举效率)。
    • ord[n]:存储货车的废物类型序列(0-based索引)。
    • Ans结构体:存储最优解(处理数量、三个设置编号),重载<运算符确保优先选择“处理量最大→天数最少”的解。

    2. 单设置处理

    • 遍历每个设置s1,贪心处理货车直到遇到无法处理的货车,记录最大处理量firbad[s1]
    • 直接更新单设置情况下的最优解。

    3. 双设置处理

    • 对于每个设置s1,找到其第一个无法处理的废物类型b
    • 枚举所有能处理b的设置s2,贪心处理s1s2可处理的所有货车,记录最大处理量firbad2[s1][i]
    • 更新双设置情况下的最优解。

    4. 三设置处理

    • 基于双设置的结果,找到s1+s2组合下第一个无法处理的废物类型c
    • 枚举所有能处理c的设置s3,分两阶段贪心处理(先s1+s2,再s2+s3),确保辅助轨道为空。
    • 更新三设置情况下的最优解。

    5. 结果输出

    • 输出最大处理量,以及对应的三个设置编号(注意+1恢复原输入的1-based编号)。
    • 若天数不足3天,未使用的设置编号自动为-1+1=0,符合题目输出要求。

    测试样例验证

    输入样例

    13 5 4
    1 0
    4 5 0
    5 3 0
    2 5 0
    4 5 2 5 5 4 1 1 5 4 5 3 3
    

    输出样例

    11
    2 1 4
    

    验证过程

    • 设置2(原编号2)可处理类型4、5,处理前5辆货车(4、5、2、5、5)时,第3辆类型2无法处理,切换到设置1(原编号1,处理类型1)。
    • 设置1处理第6辆(4,无法处理)→ 切换到设置4(原编号4,处理类型2、5),继续处理剩余货车。
    • 最终共处理11辆货车,符合样例输出。

    注意事项

    1. 废物类型和设置编号均从0-based转换为1-based,确保输入输出一致。
    2. 枚举设置时利用ids[k](每种类型最多10个设置),大幅降低枚举量。
    3. 三设置处理的两阶段贪心,确保辅助轨道在第三天结束时为空,满足题目约束。
    4. Ans结构体的排序逻辑,保证优先选择“处理量最大→天数最少”的最优解。
    • 1

    信息

    ID
    5399
    时间
    2000ms
    内存
    512MiB
    难度
    10
    标签
    递交数
    3
    已通过
    1
    上传者