动态规划之01背包、完全背包、多重背包问题
一、01背包问题 :
描述:
1.输入格式:
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
2.定义变量:
V是代价上限,N是物品总量,v[N]是每个代价,w[N]是每个价值。
3.思路:
每个物品数量唯一,只能取或不取,使得在代价不越限制的情况下最终获得价值最大。
代码:
1.二维数组代码:
for(int i = 1;i <= N;i++){
for(int j = 1;j <= V;j++){
dp[i][j] = dp[i - 1][j];//第i个物品不取
if(j >= c[i])
dp[i][j] = max(dp[i][j],dp[i - 1][j - c[i]] + w[i]);
}
}
2.一维数组优化代码
for(int i = 1;i <= N;i++){
for(int j = V;j >= v[i];j--){//必须倒序输出
dp[j] = max(dp[j],dp[j - v[i]] + w[i]);
}
}
3.输出整个过程的代码:
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int Num = 1010;
int dp[Num][Num];
int v[Num],w[Num];
int N,V;
int main(){
cin >> N >> V;
for(int i = 1;i <= N;i++)
cin >> v[i] >> w[i];
for(int i = N;i >= 1;i--){
for(int j = 0;j <= V;j++){
dp[i][j] = dp[i + 1][j];//不能用一维数组
if(j >= v[i])
dp[i][j] = max(dp[i][j],dp[i + 1][j - v[i]] + w[i]);
}
}
int cur_v = V;
for(int i = 1 ; i <= N ; i++){
if(i == N && cur_v >= v[i]){//特判终点
printf("%d ",i);
break;
}
if(cur_v <= 0)
break;
if(cur_v - v[i]>=0 && dp[i][cur_v] == dp[i + 1][cur_v - v[i]] + w[i]){
printf("%d ",i);
cur_v -= v[i];
}
}
return 0;
}
二、完全背包问题:
描述:
1.输入格式:
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。
2.定义变量:
V是代价上限,N是物品总量,v[N]是每个代价,w[N]是每个价值。
3.思路:
每个物品数量不限,一定取或不取的操作后,使得在代价不越限制的情况下最终获得价值最大。
代码:
1.最朴素的三重循环二维数组:
for(int i = 1 ; i<=N ;i++)
for(int j = 0 ; j<=V ;j++)
for(int k = 0 ; k*V[i]<=j ; k++)
dp[i][j] = max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
2.二重循环二维数组:
for(int i = 1 ; i <=N ;i++)
for(int j = 0 ; j <=V ;j++)
dp[i][j] = dp[i-1][j];
if(j-v[i]>=0)
dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);
3.再优化:
for(int i = 1;i <= N;i++)
for(int j = v[i];j <= V;j++)//不必倒序
dp[j] = max(dp[j],dp[j - v[i]] + w[i]);
三、多重背包问题:
描述:
1.输入格式:
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
2.定义变量:
V是代价上限,N是物品总量,s[N]是每个物品的数量,v[N]是每个代价,w[N]是每个价值。
3.思路:
每个物品数量有限,一定取或不取的操作后,使得在代价不越限制的情况下最终获得价值最大。
代码:
1.最朴素的做法
for(int i = 1; i <= N; i ++)
for(int j = V; j >= 0; -- j)
for(int k = 1; k <= M; k ++)
if(j >= c[i][k])
dp[j] = max(dp[j], dp[j - c[i][k]] + w[i][k]);
2.二进制拆分
举例,如13=23+22+2^0+6=1+2+4+6;
在0~13中,任意一个整数都可以分解成1、2、4、6的选排列之一的和
转化成01背包问题
代码:
//将每种物品根据物件个数进行打包
int cnt = 0;
for(int i = 1; i <= N; i ++){
int a, b, s;
cin >> a >> b >> s;
int k = 1;
while(k <= s){
cnt ++;
v[cnt] = k * a;
w[cnt] = k * b;
s -= k;
k *= 2;
}
if(s > 0){
cnt++;
v[cnt] = s * a;
w[cnt] = s * b;
}
}
//多重背包转化为01背包问题
for(int i = 1; i <= cnt; i ++)
for(int j = V; j >= v[i]; j --)
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
求点赞
↓↓↓↓↓

浙公网安备 33010602011771号