分组背包

本篇参考自传送门

问题

 有N件物品和一个容量为V的背包。第i件物品的费用是w [ i ],价值是v[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

思想

 这个问题就转化为了每一组都可以有多种取法,但是只能取一个物品,也可以不取,则可以设 dp[i,j] 表示前i 组物品容量为 j 时的最大收益

则状态转移方程为
dp[k][j]=max(dp[k−1][j],dp[k−1][j−c[i]]+w[i]∣物品i属于组k)

伪代码

for( 1 - n)//所有的组
	for( v  - 0)//枚举所有容量
		for( 1 - 该组所有物品数量 )
			dp[j] = max{dp[j], dp[j - w[i]] + v[i]}

其中要注意 枚举每组数量的循环一定是要在枚举容量的内层,不然会出现一组中多于一个物品被取出

这样枚举就保证了每一个状态下每个组中最多只取一个物品,因为是每个状态下进行 多次比较,看该组内哪个物品价值更高,如果当前在该组内拿出的物品的价值大于之前在该组拿出物品的价值,就进行替换,如果枚举完该组所有物品,都不能使该状态下的价值大于之前的价值,这个组的物品就选择一个不拿。

例题

1、 xinjun与阴阳师

#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e3 + 5;

int dp[maxn],val[maxn][maxn],w[maxn][maxn];
int num[maxn];

void Solve(int n,int m)
{
    memset(dp, 0, sizeof dp);
    for(int i = 1; i <= n; i++){
        cin>>num[i];
        for(int j = 1; j <= num[i]; j++) cin>>val[i][j];
        for(int j = 1; j <= num[i]; j++) cin>>w[i][j];
    }
    for(int i = 1; i <= n; i++){
        for(int j = m; j >= 0; j--){
            for(int k = 1; k <= num[i]; k++){
                if(j >= w[i][k]) dp[j] = max(dp[j],dp[j - w[i][k]] + val[i][k]);
            }
        }
    }
    cout<<dp[m]<<endl;
}

int main()
{
    int t;
    cin>>t;
    while(t--){
        int n,m;
        cin>>n>>m;
        Solve(n,m);
    }
    return 0;
}

2、luogu P1757 通天之分组背包

#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e3 + 5;

int dp[maxn],v[maxn][maxn],w[maxn][maxn],num[maxn];

void Solve(int m,int n)
{
    int count = 0;
    memset(dp,0,sizeof dp);
    for(int i = 1; i <= n; i++){
        int a,b,c;
        cin>>a>>b>>c;
        count++;
        num[c]++;
        w[c][num[c]] = a;
        v[c][num[c]] = b;
    }
    for(int i = 1; i <= count; i++){
        for(int j = m; j >= 0; j--){
            for(int k = 1; k <= num[i]; k++){
                if(j >= w[i][k]) dp[j] = max(dp[j],dp[j - w[i][k]] + v[i][k]);
            }
        }
    }
    cout<<dp[m]<<endl;
 }

int main()
{
    int m,n;
    cin>>m>>n;
    Solve(m,n);
    return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值