Codeforces Round #383 (Div. 2)D-(并查集&分组背包)|(搜索&01背包)

58 篇文章 0 订阅
6 篇文章 0 订阅

传送门
这里写图片描述
分组背包模板题。
用并查集搞一搞一齐的关系就好。
然后就是分组背包的模板了,注意,在分组的时候,把i组所有和加起来 这一个虚拟的物品分到 i组里,而不能独立分组,不然会造成某一个模特的的多次使用。。
或者用搜索,这个搜索用滚动数组的时候,类似于树形dp里面,在一个树内进行01背包。
但是在遍历的时候,可能会出现从0数组 转到0数组的情况(1数组先前已经被vis掉了,如果我们不处理这个结果就会发生前面的结果可能没有办法继承。)所以我们每次可以 把所有的数组 都给赋值了。
这个很重要,本菜没有想到好的方法。大家可以一起交流
dfs搜索版本

#include <bits/stdc++.h>
#define MOD 1000000007
#define maxn 100005
using namespace std;
typedef long long ll;

int dp[2][1005], vis[1005];
int w[1005], b[1005], h, sum1, sum2;
vector<int> v[1005];
int n, m, ww, a, bb;
void dfs(int k,int j){
    for(int i = ww; i >= w[k]; i--){
        dp[j%2][i] = max(dp[(j-1)%2][i-w[k]] + b[k], dp[j%2][i]);
    }
    vis[k] = 1;
    sum1 += w[k];
    sum2 += b[k];
    for(int i = 0; i < v[k].size(); i++){
        if(vis[v[k][i]] == 0){
            dfs(v[k][i],j);
        }
    }

}
int main(){
  //  freopen("in.txt", "r", stdin);
    scanf("%d%d%d", &n, &m, &ww);
    for(int i = 1; i <= n; i++)
     scanf("%d", w+i);
    for(int j = 1; j <= n; j++)
     scanf("%d", b+j);
    for(int i = 1; i <= m; i++){
        scanf("%d%d",&a,&bb);
        v[a].push_back(bb);
        v[bb].push_back(a);//建图,然后用搜索的方法处理,类似树形dp
    }
    for(int i=1;i<=n;i++){
        sum1=0;sum2=0;
        //memset(vis,false,sizeof(vis));
        if(!vis[i])
        dfs(i,i);//一层一层的背包。
        for(int j = ww; j >= sum1; j--){
            dp[i%2][j] = max(dp[(i-1)%2][j-sum1] + sum2, dp[i%2][j]);
          }
        for(int x=1;x<=ww;x++)
         dp[(i+1)%2][x]=dp[i%2][x];
         // cout<<endl;
    }
 printf("%d\n",dp[n%2][ww]);
    return 0;
}
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <vector>
using namespace std;
/*分组背包,模板题。
*/
const int maxn=100005;
int fa[maxn];
void init(int m){
for(int i=0;i<maxn;i++)
    fa[i]=i;
}
int find1(int a){
   if(fa[a]==a)return a;
   return fa[a]=find1(fa[a]);
}
void unite(int a,int b ){
   int x=find1(a);
    int y=find1(b);
    if(x==y)return;
    fa[x]=y;
}
int c[maxn];
int v[maxn];
vector<int>G[maxn];
int dp[maxn];
int main()
{   int m,n,p;
    int a,b;
    scanf("%d%d%d",&m,&n,&p);
    init(m*2);
    for(int i=1;i<=m;i++){
        scanf("%d",&c[i]);
    }
    for(int i=1;i<=m;i++){
        scanf("%d",&v[i]);
    }
    for(int i=1;i<=n;i++){
        scanf("%d%d",&a,&b);
        unite(a,b);
    }
    int t=m;
    for(int i=1;i<=m;i++)
        G[find1(i)].push_back(i);//根据ufs关系来确定一个图图
        for(int i=1;i<=m;i++){
            int sum1=0;
            int sum2=0;
            if(!G[i].size()) continue;
            for(int j=0;j<G[i].size();j++){
                sum1+=c[G[i][j]];
                sum2+=v[G[i][j]];
            }
            if(G[i].size()<=1) continue;
            c[1+t]=sum1;
            v[++t]=sum2;
            G[i].push_back(t);
        }
        /*for(int i=0;i<=t;i++){
            cout<<i<<"@@";
            for(int j=0;j<G[i].size();j++)
             printf("%d ",G[i][j]);
             cout<<endl;
             }*/
        //cout<<m<<endl;
     for(int i=1;i<=t;i++){
         if(!G[i].size()) continue;
         for(int j=p;j>=0;j--){
            for(int k=0;k<G[i].size();k++)
                if(j>=c[G[i][k]])
                dp[j]=max(dp[j],dp[j-c[G[i][k]]]+v[G[i][k]]);
         }
     }
     printf("%d\n",dp[p]);





    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值