DP模型——背包模型 03 (混合、有依赖、背包状态初始化)

混合背包

有 N 种物品和一个容量是 V 的背包。

物品一共有三类:

  • 第一类物品只能用1次(01背包);
  • 第二类物品可以用无限次(完全背包);
  • 第三类物品最多只能用 si 次(多重背包);

每种体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

分类讨论

// Problem: 混合背包问题
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/7/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// Code by: ING__
// 
// Edited on 2021-07-27 21:53:47

#include <iostream>

using namespace std;

const int N = 1010;

int n, m;
int f[N];

int main(){
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		int v, w, s;
		cin >> v >> w >> s;
		if(s == 0){
			for(int j = v; j <= m; j++){
				f[j] = max(f[j], f[j - v] + w);
			}
		}
		else{
			if(s == -1){ // 看成特殊的多重背包
				s = 1;
			}
			
			// 多重背包二进制优化
			for(int k = 1; k <= s; k *= 2){
				for(int j = m; j >= k * v; j--){
					f[j] = max(f[j], f[j - k * v] + w * k);
				}
				s -= k;
			}
			
			if(s){
				for(int j = m; j >= s * v; j--){
					f[j] = max(f[j], f[j - s * v] + w * s);
				}
			}
		}
	}
	
	cout << f[m];
	
	return 0;
}

有依赖的背包

有 N 个物品和一个容量是 V 的背包。

物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。

状态表示

所有从以u为根的子树中选,且总体积不超过 j 的方案

属性

MAX

状态计算

dfs在遍历到 x 结点时,先考虑一定选上根节点 x ,因此初始化 f[x][v[x] ~ m] = w[x]
在分组背包部分:
j 的范围 [ m , v[x] ] 小于v[x]则没有意义因为连根结点都放不下;
k 的范围 [ 0 , j-v[x] ],当大于j-v[x]时分给该子树的容量过多,剩余的容量连根节点的物品都放不下了;

Code
#include <iostream>
#include <vector>

using namespace std;

const int N = 110;

int n, m;
vector<int> g[N]; // 邻接矩阵存图
int f[N][N]; // 以u为根节点,容量不超过j的最大价值
int v[N];
int w[N];

void dfs(int u){
    for(int i = v[u]; i <= m; i++) f[u][i] = w[u];
    
    for(int i = 0; i < g[u].size(); i++){
        int y = g[u][i];
        dfs(y);
        for(int j = m; j >= v[u]; j--){
            for(int k = 0; k <= j - v[u]; k++){
                f[u][j] = max(f[u][j], f[u][j - k] + f[y][k]);
            }
        }
    }
}

int main(){
    cin >> n >> m;
    
    int root = 1;
    
    for(int i = 1; i <= n; i++){
        int p;
        cin >> v[i] >> w[i] >> p;
        if(p == - 1) root = i;
        else g[p].push_back(i);
    }
    
    dfs(root);
    
    cout << f[root][m];
    
    return 0;
}

状态联系

体积最多是 j;体积恰好是 j;体积至少是 j

求价值(价值最值)初始化

这里本质上都进行了降维优化,所以初始化的时候本质上是对f[0][j]进行初始化

  • 体积最多是 j 的时候,初始化全部为0,保证枚举体积 v 大于等于0

    • 因为初始化为0即根据状态表示,我们每一个方案都是在枚举过程中,如果不超过这个体积是有这个状态的合法性的,即就是选不了,最后价值是0,也满足了不超过体积 j 。

      (只会求价值的最大值)

  • 体积恰好是 j 的时候,f[0] = 0, f[i] = |INF|,保证枚举体积 v 大于等于0

    • 当f[0]的时候,即一个都不选,体积为0,显然这个时候价值就是0;当别的状态的时候,最初始的状态一定达不到恰好这个体积下有合法的价值,所以为了不选这个状态,即确定其不合法性,我们根据要求的是最大还是最小来确定其INF的正负性
  • 体积至少是 j 的时候,f[0] = 0, f[i] = INF

    • 这里的表述也有上面类似,但是这里可以不用保证体积要大于等于0,因为就像上面提到的潜水员那个题。如果体积是负数也符合至少这个条件,因为每个体积都大于0,所以如果这个状态为负数的情况下,也等价于体积为0的情况。

      而这INF的含义也是当我还没选的时候,显然这时候方案也是不合法的。即体积至少为 j ,但一个都没选,显然是不合法的

      (只会求价值的最大值)

无穷的目的是在更新的时候不使用这个值,正负取决于定义要求的是最大还是最小值

求方案数的初始化

二维情况

  • 体积至多j,f[0][i] = 1, 0 <= i <= m,其余是0
  • 体积恰好j,f[0][0] = 1, 其余是0
  • 体积至少j,f[0][0] = 1,其余是0

一维情况

  • 体积至多j,f[i] = 1, 0 <= i <= m,
  • 体积恰好j,f[0] = 1, 其余是0
  • 体积至少j,f[0] = 1,其余是0

初始化参考:https://www.acwing.com/blog/content/458/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值