搭配购买(并查集+01背包)

63 阅读2分钟

题目描述

Joe觉得云朵很美,决定去山上的商店买一些云朵。

商店里有 nn 朵云,云朵被编号为 1,2,,n1,2,…,n,并且每朵云都有一个价值。

但是商店老板跟他说,一些云朵要搭配来买才好,所以买一朵云则与这朵云有搭配的云都要买。

但是Joe的钱有限,所以他希望买的价值越多越好。

输入格式

第 11 行包含三个整数 nmwn,m,w,表示有 nn 朵云,mm 个搭配,Joe有 ww 的钱。

第 2n+12∼n+1 行,每行两个整数 cidic_i,d_i, 表示 ii 朵云的价钱和价值。

第 n+2n+1+mn+2∼n+1+m 行,每行两个整数 uiviu_i,v_i,表示买 uiu_i 就必须买 viv_i,同理,如果买 viv_i 就必须买 uiu_i

输出格式

一行,表示可以获得的最大价值。

数据范围

1n100001≤n≤10000,
0m50000≤m≤5000,
1w100001≤w≤10000,
1ci50001≤c_i≤5000,
1di1001≤d_i≤100,
1ui,vin1≤u_i,v_i≤n,

输入样例:

5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2

输出样例:

1

题目分析

本题的解题思路是先用 并查集 将物品重新分类,然后再用 01背包 选择物品。

分析题目,每个物品可看作拥有体积 vv 和价值 ww,Joe 则拥有一个体积为 mm 的背包,相较于一般的01背包,本题主要是将一些物品归为一个物品,其中体积以及价值均求和。

接下来,我们便可以使用并查集的思想,将有联系的物品的性质累加到这条关系链中的一个物品之上,注意每个物品对此关系链的根节点的贡献只有一次。

然后再利用01背包的思想,枚举所有物品链的根节点 (x=p[x])(x=p[x]),对其进行选择。

最终复杂度为 O(nm)O(nm)

Accept代码

#include <bits/stdc++.h>

using namespace std;

const int N = 10010;

int n, q, m;
int v[N], w[N];
int p[N], f[N];

int find(int x)
{
    if (x != p[x]) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    cin >> n >> q >> m;
    for (int i = 1; i <= n; i ++) cin >> v[i] >> w[i], p[i] = i;
    while (q --)
    {
        int a, b;
        cin >> a >> b;
        a = find(a), b = find(b);
        if (a == b) continue;
        p[b] = a;
        w[a] += w[b];
        v[a] += v[b];
    }
    for (int i = 1; i <= n; i ++)
    {
        if (find(i) != i) continue;
        // cout << i << ' ' << v[i] << ' ' << w[i] << "\n";
        for (int j = m; j >= v[i]; j --)
            f[j] = max(f[j], f[j - v[i]] + w[i]);
    }
    cout << f[m];
    return 0;
}

碎碎念

下午时,坐在宿舍阳台上,听着校园的广播,云朵确实很美~

只是不知道这样的日子还有多少