0-1背包dp问题

问题描述:

给定n种物品和一背包。物品i的体积是wi,其价值为vi,背包的容量为C。问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
注:物品只能选择不装或者装入背包,而不分切一小部分装入,即0或者1。因此,0-1背包问题是一个特殊的整数规划问题。
形式化描述:
输入:C > 0, wi > 0, vi > 0, 1≤ i ≤ n;
输出:(x1, x2, …, xn),xi∈{0, 1},使得∑1≤i≤n vi·xi最大。

(1)描述0-1背包问题的最优子结构,并利用“剪切-粘贴”技术证明;
(2)利用最优子结构,写出原问题最优解的递归表达式;
(3)利用自底向上的方法计算最优解的值和最优解,用伪代码进行描述,并分析算法的时间复杂度;
(4)用高级编程语言实现(3)中的求解0-1背包问题的动态规划算法,并通过问题实例测试程序,对运行结果截图。


0-1背包问题是很典型的动态规划问题,若对背包的限定条件再加上重量限制等又可变为二重0-1背包问题,但是万变不离其宗。
二重0-1背包问题:https://blog.csdn.net/worthyyyou/article/details/112846886
一重0-1背包问题:https://blog.csdn.net/qq_50985215/article/details/125879917

本笔记是算法导论实验的粗略版本,有空时更改更好的排版。尽量按照算法导论书上四个步骤来解答,只是将leetcode上那种状态转移方程式等术语用算法导论的术语表达。

1.最优子结构

首先看原问题,是个典型的0-1规划问题,数学描述如下:
目标: m a x ∑ i = 1 n v i x i 约束条件: { ∑ i = 1 n w i x i ≤ C x i ∈ { 0 , 1 } , 1 ≤ i ≤ n 目标:max\sum_{i=1}^{n}v_{i}x_{i}\\ 约束条件: \begin{cases} \sum_{i=1}^{n}w_{i}x_{i}\le C\\ x_{i}\in\{0,1\},1\le i \le n \end{cases} 目标:maxi=1nvixi约束条件:{i=1nwixiCxi{0,1},1in

子问题:
目标: m a x ∑ t = 1 i v t x t 约束条件: { ∑ t = 1 i w t x t ≤ j x t ∈ { 0 , 1 } , 1 ≤ t ≤ i 目标:max\sum_{t=1}^{i}v_{t}x_{t}\\ 约束条件: \begin{cases} \sum_{t=1}^{i}w_{t}x_{t}\le j \\ x_{t}\in\{0,1\},1\le t \le i \end{cases} 目标:maxt=1ivtxt约束条件:{t=1iwtxtjxt{0,1},1ti

利用“剪切-粘贴”证明最优子结构性:

暂略,有空补(不难,根据ppt上照搬)

2.递归定义最优解的值

最优值记为m(i,j),为背包容量为j,可选择物品为1,2,,,i是0-1背包问题的最优解。递归式如下:
m ( i , j ) = { m a x { m ( i − 1 , j ) , m ( i − 1 , j − w i ) + v i } , w i ≤ j m ( i − 1 , j ) , 0 ≤ j ≤ w i m(i,j)= \begin{cases} max\{m(i-1,j),m(i-1,j-w_{i})+v_{i}\},w_{i}\le j\\ m(i-1,j),0\le j \le w_{i} \end{cases} m(i,j)={max{m(i1,j),m(i1,jwi)+vi},wijm(i1,j),0jwi
解释:
当考虑前 i 种物品时,对于第 i 种物品,有两种方法,一种是可以加入背包(条件就是自身体积必须小于当前容量),一种是不能能加入背包(条件是自身容积容量大于限制条件,因此,它的值和考虑前 i - 1种物品的值是一样的 )。
当可以加入背包时,要考虑两种情况,进而来选出最大值。一种时不加入第 i 种物品时的值,一种是加入第 i 种物品的值,加入i商品的值就等于i的本身的 v i v_{i} vi值加上第i-1的物品时容量已为 j − w i j-w_{i} jwi的最优值。

3.计算最优解的值

采用自底向上的方法

伪代码:
暂略

c++实现:

#include
#include  
#define MAX 10005
using namespace std;

int maxx(int x,int y){
	if(x <= y){
		return y;
	}
	else{
		return x;
	}
}
 
int backpack_dp(int n,int c,int w[],int v[]){
	int dp[50][50] = { 0 };      //即上面提到的m(i,j)
 
	for (int i = 1; i <= n; i++){
		for (int j = 1; j <= c; j++){
				if (w[i] <= j){
					dp[i][j] = maxx(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
				}
				else{
					dp[i][j] = dp[i - 1][j];
				}
		}
	}
	
	cout << "背包能放物品的最大价值为:" << dp[n][c] << endl;

    //构造最优解
	int x[MAX] = { 0 };   //记录是否被选中
	for (i = n; i > 1; i--)
		if (dp[i][c] == dp[i - 1][c])
			x[i] = 0;
		else {
			x[i] = 1; 
			c -= w[i];
		}
		//当dp[1][c]为空值(0)时x[1]为0,不为空值(0)则为1
		x[1] = (dp[1][c]) ? 1 : 0;
		if 
	cout << "被选入背包的物品的编号,体积,价值分别是:" << endl;
	for (i = 1; i< n + 1; i++)
		if (x[i] == 1)
			cout << "第" << i << "个物品:" << w[i] << " " << " " << v[i] << endl;
 
}
int main()
{
	int n, c;
	int w[MAX] = { 0 };   //体积
	int v[MAX] = { 0 };   //价值
	cout << "请输入物品个数:";
	cin >> n;
	cout << "请输入背包的容量:";
	cin >> c;
	cout << "请依次输入各个物品的重量,价值:(共" << n << "个)" << endl;
	for (i = 1; i < n + 1; i++)
	{
		cin >> w[i] >> v[i];
	}
	backpack_dop(n,c,w,v);
	return 0;
}

复杂度:
粗略根据for的嵌套次数得出是 O ( n 2 ) O(n^{2}) O(n2)

4.构造最优解

//构造最优解
	int x[MAX] = { 0 };   //记录是否被选中
	for (i = n; i > 1; i--)
		if (dp[i][c] == dp[i - 1][c])
			x[i] = 0;
		else {
			x[i] = 1; 
			c -= w[i];
		}
		//当dp[1][c]为空值(0)时x[1]为0,不为空值(0)则为1
		x[1] = (dp[1][c]) ? 1 : 0;
		if 
	cout << "被选入背包的物品的编号,体积,价值分别是:" << endl;
	for (i = 1; i< n + 1; i++)
		if (x[i] == 1)
			cout << "第" << i << "个物品:" << w[i] << " " << " " << v[i] << endl;

你可能感兴趣的:(算法导论,动态规划,算法)