题意:背包问题,加了一个限制条件:某几个物品会相互限制,限制的集合里的物品只能选一件
思路:加一个并查集,把集合当成一个物品,像01背包那样记忆化搜索即可。
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
struct goods{
int v,c;
}g[1100];
vector<int> vc[1100];
int dp[1100][1100];
int pa[1100];
bool vis[1100],mark[1100];
int pre;
int dfs(int w,int cnt,int m){
int ans = 0;
if(dp[cnt][w] != -1) ans = dp[cnt][w];
else if(cnt == m){
if(cnt <= pre){
for(int i = 0;i<vc[cnt].size();i++){
int index = vc[cnt][i];
if(w>=g[index].c) ans=max(ans,g[index].v);
}
}
else{
if(w >= g[vc[cnt][0]].c)
ans += g[vc[cnt][0]].v;
}
}
else{
if(cnt <= pre){
ans = dfs(w,cnt+1,m);
for(int i =0;i<vc[cnt].size();i++){
int index = vc[cnt][i];
if(w >= g[index].c){
ans = max(ans,dfs(w-g[index].c,cnt+1,m)+g[index].v);
}
}
}
else{
if(w >= g[vc[cnt][0]].c)
ans = max(dfs(w,cnt+1,m),dfs(w- g[vc[cnt][0]].c,cnt+1,m) + g[vc[cnt][0]].v);
else
ans = dfs(w,cnt+1,m);
}
}
return dp[cnt][w] = ans;
}
int find(int x){
if(pa[x] == x)return x;
return pa[x] = find(pa[x]);
}
int main()
{
int n,Wmax,k;
while(~scanf("%d%d%d",&n,&Wmax,&k)){
memset(dp,-1,sizeof dp);
memset(mark,false,sizeof mark);
memset(vis,false,sizeof vis);
for(int i=1;i<=n;i++) vc[i].clear(),pa[i]=i;
for(int i=1;i<=n;i++) scanf("%d%d",&g[i].v,&g[i].c);
for(int i=1;i<=k;i++){
int a,b;scanf("%d%d",&a,&b);
mark[a] = mark[b]=true;
int x = find(a);int y = find(b);
if(x != y) pa[x] = y;
}
int m = 1;
for(int i=1;i<=n;i++){
int x = find(i);
if(vis[x]) continue; vis[x] = true;
if(!mark[i]) continue;
for(int j =1;j<=n;j++){
int y = find(j);
if(x == y) vc[m].push_back(j);
}
m++;
}
pre = --m;
for(int i=1;i<=n;i++){
if(!mark[i]) vc[++m].push_back(i);
}
printf("%d\n",dfs(Wmax,1,m));
}
return 0;
}