01背包变式(并查集+dp01背包)

1.分组背包不能同时选

机器人们都想知道谁是最勇敢的,于是它们比赛搬运一些物品。
它们到了一个仓库,里面有n个物品,每个物品都有一个价值Pi和重量Wi,但是有些物品放在一起会爆炸,并且爆炸具有传递性。(a和b会爆炸、b和c会爆炸则a和c会爆炸)机器人们可不想因此损失自己好不容易从Wind那里敲诈来的装备,于是它们想知道在能力范围内,它们最多可以拿多少价值的物品。
你能帮助它们吗?
 

输入

每组测试数据
第1行为n,Wmax,k(0<=n,Wmax,k<=1000)
接下来n行,为每个物品的Pi,Wi(0<=Pi<=1000,1<=Wi<=10,均为整数)
再接下来k行,每行2个数字a,b表示a和b会发生爆炸 

输出

对每组数据输出1行,为最大可能价值 

样例输入 Copy

3 10 1
100 1
200 5
10 5
1 2

样例输出 Copy

210
比如就是样例1中1和2不能同时要拿,会爆炸
这个是标准的分组背包
这个题就是把能爆炸的给连在一起,每一个组只能拿一个
acwing 中的链接
传送门
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=1e3+100;
int n,wm,k;
int pre[maxn];
int v[maxn];
int w[maxn];
int dp[maxn];
vector<int>vv[maxn];
int find(int x){
    if(pre[x]==x){
        return x;
    }
    else{
        return pre[x]=find(pre[x]);
    }
}
void merge(int x,int y){
    int f1=find(x);
    int f2=find(y);
    if(f1!=f2){
        pre[f1]=f2;//f2是根 
    }
}  
int main(){
    cin>>n>>wm>>k;
    for(int i=1;i<=n;i++){
        cin>>v[i]>>w[i];
        pre[i]=i;
    }
    int x,y;
    for(int i=1;i<=k;i++){
        cin>>x>>y;
        merge(x,y); 
    }
    for(int i=1;i<=n;i++){
        int ans=find(i);
        vv[ans].push_back(i);    
    }
    for(int i=1;i<=n;i++){
        if(vv[i].size()==0) continue;
        for(int j=wm; j>=0; j--)
        {
            for(int s=0; s<vv[i].size(); s++){
                int x=vv[i][s];
                if(j>=w[x]) dp[j]=max(dp[j],dp[j-w[x]]+v[x]);
            }
        }
    }
    
    cout<<dp[wm];
}
二:
传送门

明天就是母亲节了,电脑组的小朋友们在忙碌的课业之余挖空心思想着该送什么礼物来表达自己的心意呢?听说在某个网站上有卖云朵的,小朋友们决定一同前往去看看这种神奇的商品,这个店里有 n 朵云,云朵已经被老板编号为 1,2,3,...,n,并且每朵云都有一个价值,但是商店的老板是个很奇怪的人,他会告诉你一些云朵要搭配起来买才卖,也就是说买一朵云则与这朵云有搭配的云都要买,电脑组的你觉得这礼物实在是太新奇了,但是你的钱是有限的,所以你肯定是想用现有的钱买到尽量多价值的云。


输入格式

第一行输入三个整数,n,m,w,表示有 n 朵云,m 个搭配和你现有的钱的数目。

第二行至 n+1 行,每行有两个整数, c_i,d_i表示第i 朵云的价钱和价值。

第 n+2至 n+1+m 行 ,每行有两个整数 ui,vi。表示买第 ui 朵云就必须买第 ​vi 朵云,同理,如果买第 vi 朵就必须买第 ui 朵。


输出格式


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


输入输出样例


输入 #1
5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2

输出 #1
1

说明/提示


  • 对于 30\%30% 的数据,满足 1 le n le 1001n100;
  • 对于 50\%50% 的数据,满足 1 le n,w le 10^31n,w103,1 le m le 1001m100;
  • 对于 100\%100% 的数据,满足 1 le n le 10^41n104,0 le m le 5 imes 10^30m5×103。

也是dp加并查集就是着个dp是如果选着个组里的一个,就都要选
着个只需要,再并查集连根的时候连再一起就行
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=10005;
int n,m,k;
int pre[maxn];
int dp[maxn];
int v[maxn];
int w[maxn];
int find(int x){
    if(pre[x]==x)return x;
    return pre[x]=find(pre[x]);
}
int marge(int x,int y){
    int t1=find(x);
    int t2=find(y);
    if(t1!=t2){
        pre[t1]=t2;//t2是根
        v[t2]+=v[t1];
        w[t2]+=w[t1];
        v[t1]=0;
        w[t1]=0; 
    }
}
int main(){
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++){
        pre[i]=i;
        cin>>w[i]>>v[i];
    } 
    int u,vv;
    for(int i=1;i<=m;i++){
        cin>>u>>vv;
        marge(u,vv);
    }
    for(int i=1;i<=n;i++){
        if(w[i]!=0){
            for(int j=k;j>=w[i];j--){
                dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
            }
        }
    }
    cout<<dp[k]<<endl;
    return 0; 
    
}



原文地址:https://www.cnblogs.com/lipu123/p/14017942.html