题意:
给定n个价值为b 花费为w的物品, 然后某些物品是属于同一个组的, 给定一个花费限制V, 求在小于等于V的情况下取得到的价值最大为多少,能对于同一个组的物品,要么全取,要么只取一个。
分析:
可以采用并查集将所有的集合统计出来, 然后再将集合中所有的价值总和sumb和所有的花费总和sumw也作为一个元素加入集合。然后就是分组背包的做法,目前暂时参考背包九讲。
#include <bits/stdc++.h> using namespace std; const int maxn = 1007; int f[maxn]; //并查集 int b[maxn], w[maxn]; int n, W, m; void init() { for(int i = 1; i <= n; i++){ f[i] = i; } } int Find(int x) { if( x == f[x]) return x; else { f[x] = Find(f[x]); return f[x]; } } int dp[maxn]; void Merge(int v , int u) { int t1,t2; t1 = Find(v); t2 = Find(u); if(t1 != t2){ f[t2] = t1; } } struct s{ int w, b; s(int _w, int _b) : w(_w), b(_b){}; }; vector<s> group[maxn]; int divgroup() { for(int i = 1; i <= n;i++) { group[Find(i)].push_back(s(w[i],b[i])); } for(int i = 1; i <= n; i++) { if(!group[i].empty()) { int w_sum = 0, b_sum = 0; for(int j = 0; j < group[i].size(); j++){ w_sum += group[i][j].w; b_sum += group[i][j].b; } group[i].push_back(s(w_sum, b_sum)); } } } int main() { #if LOCAL freopen("1.txt","r",stdin); #endif // LOCAL scanf("%d %d %d", &n ,&m, &W); for(int i = 1; i <= n; i++){ scanf("%d", &w[i]); } for(int i = 1; i<= n; i++){ scanf("%d", &b[i]); } init(); for(int i = 0; i < m; i++){ int u, v; scanf("%d %d", &u, &v); Merge(u,v); } divgroup(); for(int k = 1; k <= n; k++){ if(!group[k].empty()){ for(int v = W; v >= 0; v--){ for(int i = 0; i < group[k].size(); i++){ if(v-group[k][i].w >= 0) dp[v] = max(dp[v], dp[v-group[k][i].w]+ group[k][i].b); } } } } printf("%d ", dp[W]); return 0; }