1 条题解

  • 0
    @ 2025-5-20 20:54:12

    这个问题要求计算安装所有软件包所需的最小介质更换次数。关键在于理解问题的本质,并将其转化为有效的算法。

    问题分析

    软件包分布在两张DVD上,安装过程需要遵循依赖关系,并且每次更换DVD都算一次操作。初始时驱动器为空,需要插入第一张DVD,结束时需要取出DVD,这两次也算作更换次数。

    关键点

    1. 依赖关系形成一个有向无环图(DAG)
    2. 每次只能安装同一DVD上的软件包,直到无法继续
    3. 必须从独立的软件包(入度为0)开始安装
    4. 目标是最小化更换DVD的次数

    方法思路

    通过分析,我们可以将问题转化为:在遵循依赖关系的前提下,如何安排DVD的使用顺序,使得更换次数最少。

    算法思路

    1. 使用拓扑排序处理依赖关系
    2. 维护两个队列,分别存储来自两张DVD的可安装软件包
    3. 每次尽可能多地安装同一DVD上的软件包,直到无法继续
    4. 切换到另一张DVD,重复上述过程
    5. 计算两种可能的初始DVD选择下的最小更换次数

    解决代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    #include <cstring>
    #include <queue>
    using namespace std;
    
    int d[100010], dd[100010], head[100010];
    int cnt;
    int n1, n2;
    
    struct nod {
        int v;
        int nex;
    } edge[200010];  // 注意边的数量可能达到200000
    
    queue<int> q[2];
    
    void init() {
        cnt = 0;
        memset(head, -1, sizeof(head));
        memset(dd, 0, sizeof(dd));
        memset(d, 0, sizeof(d));
    }
    
    void addEdge(int u, int v) {
        edge[cnt].v = v;
        edge[cnt].nex = head[u];
        head[u] = cnt++;
        d[v]++;
        dd[v]++;
    }
    
    void deal() {
        while (!q[0].empty()) q[0].pop();
        while (!q[1].empty()) q[1].pop();
        for (int i = 1; i <= n1 + n2; i++) {
            if (d[i] == 0) {
                if (i <= n1) q[0].push(i);
                else q[1].push(i);
            }
        }
    }
    
    int fun(int k) {
        deal();
        int ans = 0;
        while (!q[0].empty() || !q[1].empty()) {
            bool changed = false;
            while (!q[k].empty()) {
                int u = q[k].front();
                q[k].pop();
                changed = true;
                for (int i = head[u]; i != -1; i = edge[i].nex) {
                    int v = edge[i].v;
                    d[v]--;
                    if (d[v] == 0) {
                        if (v <= n1) q[0].push(v);
                        else q[1].push(v);
                    }
                }
            }
            if (changed) ans++;
            k ^= 1;
        }
        return ans;
    }
    
    int main() {
        int m;
        while (scanf("%d%d%d", &n1, &n2, &m) == 3) {
            if (n1 == 0 && n2 == 0 && m == 0) break;
            init();
            int u, v;
            for (int i = 0; i < m; i++) {
                scanf("%d%d", &v, &u);
                addEdge(u, v);
            }
            int ans1 = fun(0);
            for (int i = 1; i <= n1 + n2; i++) {
                d[i] = dd[i];
            }
            int ans2 = fun(1);
            int ans = min(ans1, ans2);
            cout << ans + 1 << endl;
        }
        return 0;
    }
    

    代码解释

    1. 数据结构:使用邻接表存储图的结构,每个节点的入度用于拓扑排序。

    2. 初始化init()函数初始化图的结构和入度数组。

    3. 添加边addEdge()函数用于添加依赖关系,更新入度。

    4. 处理队列deal()函数初始化两个队列,分别存储两张DVD上入度为0的软件包。

    5. 拓扑排序fun()函数实现拓扑排序的核心逻辑,每次尽可能多地安装同一DVD上的软件包,直到无法继续,然后切换到另一张DVD。

    6. 主函数:读取输入,初始化图,计算两种初始DVD选择下的更换次数,并输出最小值加1(考虑初始插入和最终取出)。

    通过这种方法,我们可以有效地计算出安装所有软件包所需的最小介质更换次数。

    • 1

    信息

    ID
    779
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    8
    已通过
    1
    上传者