1 条题解

  • 0
    @ 2026-10-27 12:58:44

    思路与算法

    用并查集合并必须同时购买的关系,把每个连通分量的价格、价值求和。每个分量视为一件物品,对预算做倒序01背包。

    正确性说明

    关系传递闭包内的云必须一起买,且不同分量没有绑定关系,故合法购买集合与分量子集一一对应。倒序01背包恰枚举各分量选或不选,取最大价值即原问题最优值。

    复杂度

    并查集初始化O(n),关系处理O((n+m)α(n)),分量汇总O(nα(n)),背包至多O(nW),总O((n+m)α(n)+nW),空间O(n+W)。

    易错点

    不能只合并一层关系,也不能把环重复计价。分量超过预算直接跳过;零预算可空选。累计价值用64位。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, m, W;
        cin >> n >> m >> W;
        vector<int> p(n), sz(n, 1);
        iota(p.begin(), p.end(), 0);
        function<int(int)> find = [&](int x) {
            return p[x] == x ? x : p[x] = find(p[x]);
        };
        vector<long long> cost(n), value(n);
        for (int i = 0; i < n; i++)
            cin >> cost[i] >> value[i];
        for (int i = 0, u, v; i < m; i++) {
            cin >> u >> v;
            u = find(u - 1);
            v = find(v - 1);
            if (u == v)
                continue;
            if (sz[u] < sz[v])
                swap(u, v);
            p[v] = u;
            sz[u] += sz[v];
            cost[u] += cost[v];
            value[u] += value[v];
        }
        vector<long long> dp(W + 1);
        for (int u = 0; u < n; u++)
            if (find(u) == u && cost[u] <= W)
                for (int cap = W; cap >= cost[u]; cap--)
                    dp[cap] = max(dp[cap], dp[cap - cost[u]] + value[u]);
        cout << dp[W] << '\n';
    }
    
    • 1

    信息

    ID
    2078
    时间
    1000ms
    内存
    64MiB
    难度
    普及+
    标签
    递交数
    0
    已通过
    0
    上传者