背包九讲(大雪菜 & dd_engi)

大雪菜背包九讲1 大雪菜背包九讲2
dd_engi的背包九讲,emm...这个找不到原链接

背包问题 - 求最优值(一般)

01背包
  • dp(i,j) :前i个物品,体积是j时的最大价值
  • v(i),w(i);体积,价值数组:
  • 对第i个物品,有选或者不选两种状态:dp(i)(j) = max(dp(i - 1, j),dp(i - 1,j - v(i)) + w(i));
  • dp(0,0) = 0;
	cin >> n >> m;
	for(int i = 1; i <= n; i++) cin >> v[i] >> w[i]; 
	
	for(int i = 1; i <= n; i++){//第一个物品开始选 
		for(int j = 0; j <= m; j++){//体积0-m 
			dp[i][j] = dp[i-1][j];//不选第i个 
			//剩余体积足够 -- 最大价值 
			if(j >= v[i]) dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);
		}
	}
	/*
	for(int i = 1; i <= n; i++){
		for(int j = m; j >= v[i]; j--){//大到小枚举,每个物品只选择一次 
			f[j] = max(f[j],f[j - v[i]] + w[i]);
		}
	}*/
	//res = f[m];
	res = dp[n][m];//n个物品最大价值 
	cout << res;
完全背包
  • f(i) 体积为i,最大价值(物品选择次数不限)
	cin >> n >> m;
	for(int i = 0; i < n; i++){
		cin >> v >> w;
		for(int j = v; j <= m; j++){
			f[j] = max(f[j],f[j - v] + w);
		}
	} 
	cout << f[m];
多重背包1
  • 每件物品只可以选择s次
  • f(i) 体积为i,最大价值
优化
  1. 多重背包的二进制优化(每个物品分s份 -- 01 -- 二进制 2 ^ x = s log s 上取整)
struct node{
	int v,w;
};
	
	vector<node> arr;
	cin >> n >> m;
	for(int i = 0; i < n; i++){
		cin >> v >> w >> s;
		/* 
		for(int j = m; j >= 0; j--){
			for(int k = 1; k <= s && k * v <= j; k++){
				f[j] = max(f[j],f[j - k * v] + k * w);
			}
		} */
		//二进制优化 -- 01背包 
		for(int k = 1; k <= s; k *= 2){
			s -= k;
			arr.push_back({v * k, w * k});
		} 
		if(s > 0) arr.push_back({v * s, w * s});
	}
	//2
	for(int i = 0; i < arr.size(); i++){
		for(int j = m; j >= arr[i].v; j--){
			f[j] =  max(f[j],f[j - arr[i].v] + arr[i].w);
		}
	}
	cout << f[m];
  1. 单调队列优化(以体积划分)

f[j] = max(f[j - v] + 2,f[j - 2 * v] + 2 * w,....f[j - k * v] + k * w)

f[j + v] = f[j] + w,f[j - v] + 2 * w

混合背包
  • 物品分为x类,每类可以使用y次
  • 分类使用01,完全,多重(二进制优化 - 01)
struct node{
	int num,v,w;
};
vector<node> arr;

	cin >> n >> m;
	for(int i = 0; i < n; i++){
		cin >> v >> w >> s;//s为背包类型
		if(s < 0) arr.push_back({-1,v,w});//0 1
		else if(s == 0) arr.push_back({0,v,w});//完全
		else{
			for(int k = 1; k <= s; k *= 2){//二进制优化多重背包 -- 01
				s -= k;
				arr.push_back({-1,v * k,w * k});
			}
			if(s > 0) arr.push_back({-1,v * s,w * s});
		}
	}
	
	for(auto t : arr){
		if(t.num  < 0){
			//01
			for(int j = m; j >= t.v; j--){
				f[j] = max(f[j],f[j - t.v] +t.w);//滚动数组,相当于默认 f[i][j] = f[i - 1][j]
			}
		}else{
			for(int j = t.v; j <= m; j++){//v,小到大枚举,可多次使用
				f[j] = max(f[j],f[j - t.v] + t.w);
			}
		}
	}
	cout << f[m];
二维费用背包
  • 01背包多了一个参数,修改一下01即可
	cin >> n >> v >> m;
	for(int i = 0; i < n; i++){
		int a,b,c;
		cin >> a >> b >> c;
		for(int j = v; j >= a; j--){
			for(int k = m; k >= b; k--){
				f[j][k] = max(f[j][k],f[j - a][k - b] + c);
			}
		}
	}
	cout << f[v][m];
分组背包
  • 每个物品组(每组s个物品)中只可以选一个 - 01
	cin >> n >> m;
	for(int i = 0; i < n; i++){
		cin >> s;
		for(int j = 0; j < s; j++){
			cin >> v[j] >> w[j];
		}
		for(int j = m; j >= 0; j--){
			for(int k = 0; k < s; k++){
				//每组s个物品
				if(j >= v[k]) f[j] = max(f[j],f[j - v[k]] + w[k]);
			}
		}
	}
	cout << f[m];
有依赖的背包问题 - 树形DP与分组背包结合
  • 选择物品i,必须选择i的依赖物品j
  • i,j一 一对应:依赖关系组成了一棵树
  • 物品 -- 体积 -- 决策
const int N = 105;
int root,n,m,p,idx,e[N],ne[N],h[N],v[N],w[N],f[N][N];//链式前向星存储图 
//F[I][J] 选择第i个物品,在体积最大为j时de最大价值 
//对每个节点,计算出它的子节点 -- 物品组 
void add(int p,int i){
	e[idx] = i;//v w 的 index 
	ne[idx] = h[p];
	h[p] = idx++;//更新首元素 
}
void dfs(int u){
	//对当前节点u,寻找最大子节点 
	for(int i = h[u]; i != -1; i = ne[i]){//物品组 
		int cur = e[i];//子节点index 
		dfs(cur);
		for(int j = m - v[u]; j >= 0; j--){//体积 
			for(int k = 0; k <= j; k++){//选择物品组中的某物品 
				f[u][j] = max(f[u][j],f[u][j - k] + f[cur][k]);
			}
		}
	}
	//必须先选择父节点 
	for(int i = m; i >= v[u]; i--){
		f[u][i] = f[u][i - v[u]] + w[u];
	}
	//无法选择当前父节点 - 0 
	for(int i = 0; i < v[u]; i++){
		f[u][i] = 0; 
	} 
}

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);
	memset(h,-1,sizeof(h));//
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		cin >> v[i] >> w[i] >> p;
		if(p == -1){
			root = i;
		}else{
			add(p,i);//p --> i 
		} 
	}
	dfs(root);
	cout << f[root][m] << endl;
	 
	return 0;
} 
最优方案数,最小总价值,最小总件数....
  1. 最(多)小总件数:(最多)最少放多少件物品可以装满背包 - min
  2. 字典序最小的最优方案:物品序号小-大枚举,若存在选择小序号的最优方案,则小序号必选
  3. 方案数:f(i,j)存储选择i个物品,最大体积为j的方案数
f[0][0] = 1
f[i][j] = f[i - 1][j] + f[i - 1][j - v[i]]
搜索解决背包问题
  1. 01背包
void dfs(i,cur_v,cur_w){
    if(i > n){
        if(cur_w > best) best = cur_w;
        return;
    }
    if(cur_v + v[i] <= v){//选择此物品
        dfs(i + 1, cur_v + v[i],cur_w + w[i]);
    }
    dfs(i + 1,cur_v,cur_w);
}
posted @ 2020-04-15 13:40  jimmy-cat  阅读(796)  评论(0编辑  收藏  举报