背包问题的个人理解(动态规划)

背包问题

01背包问题:

01背包问题描述:有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,每件物品数量只有一个,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?

(n个重量和价值分别为wiVi的物品。从这些物品中挑选出重量不超过W的物品,求所有挑选方案中价值总和的最大值。)

动态规划的基本思路:将该问题转换成子问题,考虑五件物品在给定承重 W 的背包下最大价值为原问题,如下表所示,即为考虑abcdeW = 10时的最大价值,假设为f[5][10],原问题的解可以分解为两种情况,第一种情况是不考虑放入a只考虑放入bcde承重为W时的最大价值f[4][W],第二种情况是考虑放入a时的最大价值,即value[a]+f[4][10-weight[a]] 原问题的解f[5][10]取上述两种情况中的最大值,即f[5][10] = max{f[4][10], value[a]+f[4][10-weight[a]]} 由此可以看出里面涉及到需要计算f[4][10]f[4][10-weight[a]]f[4][4]等子问题。 以此类推,自顶向下的分析可以看出原问题需要子问题的解,我们需要先计算出子问题的解,自底向上求解。求解方式如下表所示,顺序是自底向上、从左往右,或者从左往右、自底向上都可以。注意此问题中的abcde可以包含相同的物件,它们之间的顺序也可以是任意的,不影响最终的结果。

name

weight

value

0

1

2

3

4

5

6

7

8

9

10

e

4

6

0

0

0

0

6

6

6

6

6

6

6

d

5

4

0

0

0

0

6

6

6

6

6

10

10

c

6

5

0

0

0

0

6

6

6

6

6

10

11

b

2

3

0

0

3

3

6

6

9

9

9

10

11

a

2

6

0

0

6

6

9

9

12

12

15

15

15

 

 

 

 

 

 

 

 

 

 

 

 

 

 

01背包代码之1:

//用dp针对不同的重量限制计算最大的价值
//dp[i+1][j]:表示从0到i这i个物品中选出重量不超过j的物品时的总价值的最大值
//dp[0][j]=0;
const int maxn = 100;
int dp[maxn][maxn];   //dp数组
int w[maxn];          //物品重量
int v[maxn];          //物品价值
int W;                //背包能承受的最大重量
int n;                //物品个数
void solve()
{
	memset(dp, 0, sizeof(dp));
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j <= m; j++)
		{
			if (j < w[i])
				dp[i][j] = dp[i-1][j];
			else
				dp[i][j] = max(dp[i-1][j], dp[i-1][j - w[i]] + v[i]);
		}
	}
	//printf("%d\n", dp[n][W]);
}

01背包代码之2:

//当重量的数组规模太大不够用时,可以改变dp的对象,用dp针对不同价值计算最小的重量
//dp[i+1][j]:前i个物品挑选出价值总和为j时的总重量的最小值(不存在时就是一个充分大的数值INF)
//dp[0][0]=0;
//dp[0][j]=INF;
const int maxn = 100;
int dp[maxn][maxn*maxn+1];   //dp数组
int w[maxn];          //物品重量
int v[maxn];          //物品价值
int W;                //背包能承受的最大重量
int n;                //物品个数
void solve()
{
	fill(dp[0], dp[0] + maxn*maxn + 1, INF);
	dp[0][0] = 0;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j <= maxn*maxn; j++)
		{
			if (j < v[i])
				dp[i + 1][j] = dp[i][j];
			else
				dp[i + 1][j] = min(dp[i][j], dp[i][j - v[j]] + w[j]);
		}
	}
	int res = 0;
	for (int i = 0; i <= maxn*maxn;i++)
	if (dp[n][i] <= W)
		res = i;
	printf("%d\n",res);
}

完全背包问题:

完全背包问题描述:有编号分别为a,b,c,d的四件物品,它们的重量分别是2,3,4,7,它们的价值分别是1,3,5,9,每件物品数量无限个,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?

(n种重量和价值分别为wivi的物品。从这些物品中挑选重量不超过W的物品,求出挑出物品价值总和的最大值。每件物品可以挑选任意多件。)

完全背包问题与01背包问题的区别在于每一件物品的数量都有无限个,而01背包每件物品数量只有一个。

问题解法其实和01背包问题一样,只是初始化的值和递推公式需要稍微变化一下。初始化时,当只考虑一件物品a时,f[1][j] = j/weight[a] 递推公式计算时,f[i][y] = max{f[i-1][y], (f[i][y-weight[i]]+value[i])},注意这里当考虑放入一个物品 i 时应当考虑还可能继续放入 i,因此这里是f[i][y-weight[i]]+value[i], 而不是f[i-1][y-weight[i]]+value[i]

name

weight

value

0

1

2

3

4

5

6

7

8

9

10

a

2

1

0

0

1

1

2

2

3

3

4

4

5

b

3

3

0

0

1

3

3

4

6

6

7

9

9

c

4

5

0

0

1

3

5

5

6

8

10

10

11

d

7

9

0

0

1

3

5

5

6

9

10

10

12

 

 

 

 

 

 

 

 

 

 

 

 

 

 

完全背包代码:

//dp[i+1][j]:表示从前i个物品中选出重量不超过j的物品时的总价值的最大值
//dp[0][j]=0;
const int maxn = 100;
int dp[maxn][maxn];   //dp数组
int w[maxn];          //物品重量
int v[maxn];          //物品价值
int W;                //背包能承受的最大重量
int n;                //物品个数
void solve()
{
	fill(dp[0], dp[0] + W + 1, INF);  //注意初始化的问题
	dp[0][0] = 0;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j <= W; j++)
		if (j < w[i])
			dp[i][j] = dp[i - 1][j];
		else
			dp[i][j] = max(dp[i - 1][j], dp[i][j - w[i]] + v[i]);
	}
	//printf("%d\n", dp[n][W]);
}

多重背包问题:

多重背包问题描述:有编号分别为a,b,c的三件物品,它们的重量分别是122,它们的价值分别是61020,他们的数目分别是1052,现在给你个承重为 8 的背包,如何让背包里装入的物品具有最大的价值总和?

多重背包和01背包、完全背包的区别:多重背包中每个物品的个数都是给定的,可能不是一个,绝对不是无限个。

将原数量为k的物品拆分成若干组,每一组可看成一件新的物品,其价值和重量为改组中所有物品的价值重量的总和,每组物品包含的原物品个数分别为:124...k-2^c+1,其中c为使k-2^c+1大于0的最大整数。这样就将物品数量大大降低,同时通过对这些若干个原物品组合得到的新物品的不同组合,可以得到0k之间的任意件物品的价值重量和,所以对所有这些新物品做0-1背包,即可得到多重背包的解。转化之后的时间复杂度为O(V*∑ni=1log2(ki))

int v[maxn];
int w[maxn];
int num[maxn];
int dp[maxn];
void zp(int wei, int val, int tot) {//01(质量,价值,容量)
	for (int i = tot; i >= wei; i--)
		dp[i] = max(dp[i], dp[i - wei] + val);
}
void cp(int wei, int val, int tot) {//完全(质量,价值,容量)
	for (int i = wei; i <= tot; i++)
		dp[i] = max(dp[i], dp[i - wei] + val);
}
void mp(int wei, int val, int num, int tot) {//多重(质量,价值,数量,容量)
	if (wei * num >= tot) cp(wei, val, tot);
	else {
		int k = 1;
		while (k <= num) {
			zp(k * wei, k * val, tot);
			num -= k;
			k *= 2;
		}
		zp(num * wei, num * val, tot);
	}
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值