背包问题(多重背包与分组背包)
多重背包问题
与01背包的区别在于每个物品的个数有限制,且不一样。
f[i, j] = max(f[i-1,j-v[i]*k] + w[i]*k, k为选择放进背包里的当前物品的个数)
优化过程
对比两个状态转移方程
//其中s代表对于第i个物品而言限制的最大数量
f[i,j] = max(f[i-1, j], f[i-1, j-v]+w, f[i-2, j-2v]+ 2w,...,f[i-1, j-sv]+sw)
f[i, j-v] = max(f[i-1, j-v], f[i-1, j-2v]+w, f[i-1, j-3v]+2w,...,f[i-1,j-sv]+(s-1)w, f[i-1,j-(s+1)v]+sw)
发现
因此无法利用完全背包的思维进行优化。
在多重背包中,常用的优化方法为二进制优化。
例:假设当前物品最多只能选1023件,可以将其拆分成:
数量为1, 2, 4, 8, ... 512的物品,每件物品最多只能选一次。
这样就能表示出来选取数量的所有方案
当s = 200时,应拆分为1, 2, 4, 8, 16,32, 64, 73(注意73)
代码做法:
先对所有物品,按照限制的数量
再对所有物品拆分完之后的物品进行01背包的做法即可。
这样可以把枚举数量的第三层循环从
分组背包问题
所有物品被分为几组,每一组的数量有限制,问最大的价值为多少。
状态转移方程
//k为在该组内限制数量的范围枚举
f[i, j] = max(f[i-1, j-v[i,k]+w[i,k]])
习题
1. 通天之分组背包
题目背景
直达通天路·小 A 历险记第二篇
题目描述
自
输入格式
两个数
接下来
输出格式
一个数,最大的利用价值。
样例 #1
样例输入 #1
45 3
10 10 1
10 5 1
50 400 2
样例输出 #1
10
提示
int
范围内。
参考代码
//P1757
//分组背包
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int f[105][1005];
struct wp{
int a, b, c, d;
}w[1005];
bool cmp(wp x, wp y){
return x.c < y.c;
}
int main(){
int m, n;
cin >> m >> n;
for(int i = 1; i <= n; i++) cin >> w[i].a >> w[i].b >> w[i].c;
sort(w+1, w+n+1, cmp);
int num = 1;
w[1].d = num;
for(int i = 2; i <= n; i++){
if(w[i].c != w[i-1].c) num ++;
w[i].d = num;
}
for(int i = 1; i <= n; i++){
int idx = w[i].d;
for(int j = 0; j <= m; j++){
f[idx][j] = max(f[idx][j], f[idx-1][j]);
if(j >= w[i].a) f[idx][j] = max(f[idx][j], f[idx-1][j-w[i].a] + w[i].b);
}
}
cout << f[w[n].c][m] << endl;
return 0;
}
2. 搭配购买
题目描述
明天就是母亲节了,电脑组的小朋友们在忙碌的课业之余挖空心思想着该送什么礼物来表达自己的心意呢?听说在某个网站上有卖云朵的,小朋友们决定一同前往去看看这种神奇的商品,这个店里有
输入格式
第一行输入三个整数,
第二行至
第
输出格式
一行,表示可以获得的最大价值。
样例 #1
样例输入 #1
5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2
样例输出 #1
1
提示
- 对于
的数据,满足 ; - 对于
的数据,满足 , ; - 对于
的数据,满足 , 。
参考代码
//P1455
#include<stdio.h>
#include<iostream>
using namespace std;
int fa[10005], buy[10005], val[10005];
int f[10005];
bool flag[10005];
int find(int x){
if(x == fa[x]) return x;
return fa[x] = find(fa[x]);
}
void merge(int x, int y){
buy[find(y)] += buy[find(x)];
val[find(y)] += val[find(x)];
fa[find(x)] = find(y);
}
int main(){
int n, m, w;
int u, v;
cin >> n >> m >> w;
for(int i = 1; i <= n; i++){
cin >> buy[i] >> val[i];
fa[i] = i;
}
for(int i = 1; i <= m; i++){
cin >> u >> v;
if(find(u) != find(v)) merge(u, v);
}
for(int i = 1; i <= n; i++){
int ff = find(i);
if(!flag[ff]){
for(int j = w; j >= buy[ff]; j--){
f[j] = max(f[j], f[j-buy[ff]] + val[ff]);
}
flag[ff] = 1;
}
}
cout << f[w] << endl;
return 0;
}
3. [USACO2.2] 集合 Subset Sums
题目描述
对于从
如果
给出
输入格式
输入文件只有一行,且只有一个整数
输出格式
输出划分方案总数。
样例 #1
样例输入 #1
7
样例输出 #1
4
提示
【数据范围】
对于
翻译来自NOCOW
USACO 2.2
参考代码
//P1466
//题意转换为
//1~n中选取k个数,其和为(1+n)*n/4的情况总数
#include<stdio.h>
#include<iostream>
using namespace std;
typedef long long ll;
ll f[50][2500];
int main(){
int n, sum;
cin >> n;
if((1 + n) * n / 2 % 2 == 1){
cout << 0 << endl;
return 0;
}
sum = (1 + n) * n / 4;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= sum; j++){
if(j <= i){
f[i][j] = max(f[i][j], f[i-1][j]);
if(j == i) f[i][j] ++;
}
else f[i][j] = f[i-1][j-i] + f[i-1][j];
}
}
cout << f[n][sum] / 2 << endl;
return 0;
}
4. 宝物筛选
题目描述
终于,破解了千年的难题。小 FF 找到了王室的宝物室,里面堆满了无数价值连城的宝物。
这下小 FF 可发财了,嘎嘎。但是这里的宝物实在是太多了,小 FF 的采集车似乎装不下那么多宝物。看来小 FF 只能含泪舍弃其中的一部分宝物了。
小 FF 对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小 FF 有一个最大载重为
输入格式
第一行为一个整数
接下来
输出格式
输出仅一个整数,表示在采集车不超载的情况下收集的宝物的最大价值。
样例 #1
样例输入 #1
4 20
3 9 3
5 9 1
9 4 2
8 1 3
样例输出 #1
47
提示
对于
对于
#include<iostream>
using namespace std;
struct wp{
int v, w, m;
}p[100005], er[1000];
int f[400005];
int main(){
int n, w;
int idx = 0;
cin >> n >> w;
for(int i = 1; i <= n; i++) cin >> p[i].v >> p[i].w >> p[i].m;
for(int i = 1; i <= n; i++){
//将数量进行二进制处理
idx ++;
er[idx].w = p[i].w;
er[idx].v = p[i].v;
int temp = 1,sum = 1;
while(sum + temp * 2 <= p[i].m){
idx ++;
er[idx].w = er[idx-1].w*2;
er[idx].v = er[idx-1].v*2;
temp *= 2;
sum += temp;
}
int cha = p[i].m - sum;
if(cha > 0){
idx ++;
er[idx].w = p[i].w * cha;
er[idx].v = p[i].v * cha;
}
}
//转换成01背包问题
for(int i = 1; i <= idx; i++){
for(int j = w; j >= 0; j--){
if(j >= er[i].w) f[j] = max(f[j], f[j-er[i].w] + er[i].v);
}
}
cout << f[w] << endl;
}
本文作者:hsy2093
本文链接:https://www.cnblogs.com/hsy2093/p/18240035
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步