动态规划之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]);


求点赞

↓↓↓↓↓

posted @ 2023-08-23 09:26  CultReborn  阅读(13)  评论(0)    收藏  举报