叶天帝为天下苍生血战黑暗至尊(又名0-1背包问题)

在这里插入图片描述

动态规划解决0-1背包问题


问题引入:
  话说,有一天,叶天帝打算带领天庭众人去扫平禁地,斩杀发起黑暗动乱的至尊,毫无疑问,这将是一场恶战,叶天帝当然要好好准备一翻。
  叶天帝就找来了段德,索要其宝物,段德迫于众人威胁,就忍痛拿出了珍藏许久的宝贝,叶天帝十分高兴,准备都装入空间戒指,出发征战禁地。这时,一旁的大黑狗突然嘿嘿怪笑,叶天帝猛然抬头,想到了什么,原来,叶天帝最大的空间戒指被大黑狗叼走了。众人一阵无语,大家都知道,只要进了大黑狗的嘴,就休想再拿回来,叶天帝无奈,只能在一个很小的空间戒指中装征战禁地的资源,但是空间有限,战争又很残酷,叶天帝就想在有限的空间内装下最大价值的资源。
  叶天帝找众人商量,如何能使得有限的空间戒指装下最大价值的资源呢?众人皆挠头。叶天帝无奈,只能自创功法。在闭关九九八十一日之后,叶天帝出关,顿时世间霞光万道,困扰众人的问题已被破解,叶天帝将这一创世功法命名为 0-1背包大法
  这时,一旁的大黑狗突然贼溜溜盯着叶天帝,突然开口:“叶小子的脑袋怎么闪我眼睛呢?”。叶天帝顿时暴怒,想要烤了大黑狗…………


什么是 0-1 背包问题?

问题:
  给定 N个物品和一个背包。第 i i 个物品的重量为 W i W_i ,其价值为 V i V_i ,背包的总容量为 C C 。如何选取物品装入背包,使得背包中所装入的物品的总价值最大?
  需要注意的是,0-1 背包问题在选择装入背包的物品时,对于物品 i i ,只有两种选择,要么装入背包,要么不装入背包。不能将物品 i i 装入多次,也不能只装入物品 i i 的一部分。


【解题思路】:
  我们要用动态规划来解决 0-1 背包问题,可以先使用递归的思想,看能不能得到一个递推方程,然后根据这个递推方程再进行时间和空间上的优化。
  在 0-1 背包问题中有 N 个物品,我们设定一个数组 W [ i . . . N 1 ] W[i...N-1] 来表示物品 i i 的体积,数组 V [ i . . . N 1 ] V[i...N-1] 来表示物品 i i 的价值,再创建一个二维数组 D P [ N ] [ M ] DP[N][M] ,N 为物品的个数,M 为背包当前的容量,表示当前的最大价值。。例: D P [ i ] [ j ] DP[i][j] 表示取前 i i 个物品,使其体积不超过 j j 的最优解,即:最大价值。
  写出递推方程:

D P [ k ] [ w ] = { D P [ k 1 ] [ w ] 第k件太重,超过背包容量 m a x { D P [ k 1 ] [ w ] D P [ k 1 ] [ w W [ k ] ] + V [ k ] } 选和选中取较大者 DP[k][w] = \begin{cases} DP[k-1][w] & \text{第k件太重,超过背包容量} \\ max\{DP[k-1][w],DP[k-1][w-W[k]]+V[k]\} & \text{选和选中取较大者} \end{cases}
现给出背包容量 C C = 20,物品的体积和价值如下:

序号 物品体积 ( W [ i ] W[i] ) 物品价值 ( V [ i ] V[i] )
0 2 3
1 3 4
2 4 5
3 5 8
4 9 10

写出递归树:(画的多了容易混,就只写出来部分)

在这里插入图片描述

  • 我们依然采用我们的 选 和 不选 大法

什么?你不知道这个功法?请点击下面链接进行升级打怪

试炼古境

  • 分析:
    (1)选,表示选择该物品,然后在前 k 1 k-1 个物品中继续选,背包容量则应减去该物品的体积,总价值就加上该物品的价值。
    (2)不选:表示不选择该物品,然后在前 k 1 k-1 个物品中继续选择,背包容量和价值不变。
    (3)在选和不选之间取较大者。
    (4)还有一种情况就是:当前物品的体积已经超过了当前的背包容量,则直接跳过,在前面 k 1 k-1 个物品中继续选择。

填写出DP数组,即: D P [ i ] [ j ] DP[i][j] 表示取前 i i 个物品,使其体积不超过 j j 的情况下,背包中的最大价值。
在这里插入图片描述
根据上面的分析,我们可以写出递推程序:

#include<iostream>
using namespace std;
const int N = 6;//物品数量
const int C = 20;//背包容量
int DP[N][C+1]={0,0};//最大价值
int W[N] = { 0,2,3,4,5,9 };//物品的体积
int V[N] = { 0,3,4,5,8,10 };//物品的价值
int X[N];//构造最优解
int max(int a, int b)
{
	int m = a > b ? a : b;
	return m;
}
void KnapSack()
{
	int k, w;//第k个商品,j为当前容量
	int i, j;
	for (k = 1; k < N; k++)
	{
		for (w = C; w>=0; w--)
		{
			if (W[k] > w)
				DP[k][w] = DP[k - 1][w];
			else
			{
				int a = DP[k - 1][w - W[k]] + V[k];
				int b = DP[k - 1][w];
				DP[k][w] = max(a, b);
			}
		}
	}
}
void Traceback() //选择了哪些物品
{
	int c = C, n = N;
	for (int i = n; i >=0; i--)
	{
		if (DP[i][c] == DP[i - 1][c])
			X[i] = 0;
		else
		{
			X[i] = 1;
			c = c - W[i];
		}
	}
	X[n] = (DP[1][c])==0 ? 1 : 0;
}

int main()
{
	KnapSack();
	int i, j;
	Traceback();
	for (i = 0; i < N; i++)
	{
		if (X[i] == 1)
		{
			cout << "选择第" << i << "件物品:";
			cout << "体积:" << W[i] << "  " << "价值:" << V[i] << endl;
		}

	}
	cout <<"\n"<<"最大价值为:"<< DP[N-1][C] << endl;
	
	return 0;
}

输出:
选择第1件物品:体积:2 价值:3
选择第3件物品:体积:4 价值:5
选择第4件物品:体积:5 价值:8
选择第5件物品:体积:9 价值:10
最大价值为:26

扫描二维码关注公众号,回复: 9990604 查看本文章

【注】:
  我们设置物品时,第一个要设置为一个假想物品,即:它的体积为0,价值为0。为什么要这么设置呢?难道是玄学的问题?No,No,No。
  因为我们下面递推时,要从第一个物品开始(下标从0开始),因为如果从 0 开始的话,int a = DP[k - 1][w - W[k]] + V[k];,这一句就会发生下标是负数的情况。为了避免这种情况,我们就设置一个假想的物品,体积和价值都为0,它只起到一个占位的作用。


在这里插入图片描述

但是呢,这个算法用到了一个二维数组,嗯?二维……如果物品很多呢,如果背包很大呢?是不是就得开辟一个灰常大的二维数组,显然,这不是我们想要的。在这里插入图片描述


优化空间的 0-1背包

我们分析以上代码,发现一个惊天大秘密,每次计算最大价值时,它只和它的正上方元素和左上方元素相关,而且每次计算完成之后,该位的值就会更新,所以,我们是不是可以用一个一维数组来代替这个二维数组呢?
  我们设定一个一维数组 dp[C+1],为什么是数组大小是 C+1 呢?因为下标从0开始,dp[C] 才是我们要的结果。
  dp[j] 就表示背包容量为 j j 时,所获得的最大价值。


#include<iostream>
using namespace std;
const int N = 6;//物品数量
const int C = 20;//背包容量
int dp[C + 1] = { 0};//最大价值
int W[N] = { 0,2,3,4,5,9 };//物品的体积
int V[N] = { 0,3,4,5,8,10 };//物品的价值
int max(int a, int b)
{
	int m = a > b ? a : b;
	return m;
}
void KnapSack()
{
	int k, w;//第k个商品,j为当前容量
	int i, j;
	for (i = 0; i < N; i++)
	{
		for (j = C; j >= W[i]; j--)
		{
			dp[j] = max(dp[j], dp[j - W[i]] + V[i]);

		}
	}
}
int main()
{
	KnapSack();
	int i, j;
	cout << "最大价值为:" << dp[C] << endl;
	return 0;
}

至此,我们可以采用一维数组来解决 0-1 背包问题。

叶天帝也带领众人去征战禁地……
战况如何?

请点击下面链接,叶天帝将使用分治大法,逐一击败众位黑暗至尊

博客主页

在这里插入图片描述


发布了61 篇原创文章 · 获赞 67 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_44755403/article/details/104978852