01背包 问题 思路 整理

问题描述:有N个物品,每种物品只要一件,和容量为W一个背包,每个物品的价值为v[i],花费为w[i](此处的花费就是大小),问,将哪些物品装入背包中使得背包中的物品价值最大。


因为每个物品只有一件,则对该物品而言只要放入背包和不放人背包两种可能。所以叫0/1背包。


对于这类问题,现在来整理一下解题思路。


w[i] 表示 第 i 个物品的 重量
v[i] 表示 第 i 个物品的 价值 
W 为 包的 容量 


f[i][j]  表示 对前 i 个物品  和容量为 j 的背包问题的 最优解


则 对于 第 i 个物品 是否放入 背包中 有两种情况:放 或则 不放


放 : f[i][j] = f[i-1][j-w[i]] + v[i]

f[i-1][j-w[i]] 表示 对 前 i-1 个物品 和容量为 j-w[i] 背包的子问题的最优解
v[i] 表示 第 i 个物品的价值

不放: f[i][j] = f[i-1][j]  不放则对是前 i-1 个物品 和容量为 j 背包的子问题的最优解


所以 状态转移方程: f[i][j]=max{f[i-1][j],f[i-1][j-w[i]] + v[i]} 


主要伪代码:

for i from i to n
	for j from w[i] to W
		f[i][j] = max{f[i-1][j],f[i-1][j-w[i]] + v[i]}

可以对上面的算法进行空间优化!


我们可以用一维数组 dp[j] 来表示  对前 i 个物品 和 容量为 j 的背包的 最优解


循环 i 从 1 到 n,最后 输出 dp[j] 就是所求的最优解


这里的 dp[j] 就相当于 dp[i][j] 


通过上面的 态转移方程: f[i][j]=max{f[i-1][j],f[i-1][j-w[i]] + v[i]} 


我们知道 要求f[i][j] 需要 知道 f[i-1][j] 和 f[i-1][j-w[i]]


那么 求 dp[j] 就要知道 上一次循环的dp[j] 和 dp[j-w[i]] 因为 有 循环 i 从 1 到 n


那么 如果才能 让我在 更新 dp[j] 的时候 知道 上次循环的  dp[j] 和 dp[j-w[i]] 呢?


在更新 dp[j] 之前,此时的dp[j] 就是 上次循环的 dp[j]


那么 dp[j-w[i]] 呢?


我们知道 在 内循环中 更新 dp[j] 有两种 策略 : 顺序 和 逆序


如果是 顺序 ,在 求 dp[j] 时,由于 j-w[i] 小于 j 所以,dp[j-w[i]] 早已被更新了,
此时的dp[j-w[i]] 是 此次循环的 dp[j-w[i]] 而不是 上一次循环的 dp[j-w[i]],所以这种策略不行。



而 逆序的话 则 正好可行,
在更新 dp[j] 时,dp[j-w[i]] 还没被更新,此时的dp[j-w[i]] 就是上次循环的 dp[j-w[i]]。


主要伪代码如下:

for i from i to n
	for j from W to w[i]
		f[j] = max{f[j],f[j-w[i]] + v[i]}

练习题


参考代码:

#include <stdio.h>
#include <string.h>
int dp[1010];
int V[1010];
int W[1010];

int main()
{
	int cas;
	scanf("%d",&cas);
	while(cas--)
	{
		memset(dp,0,sizeof(dp));//别忘初始化 
		memset(V,0,sizeof(V));
		memset(W,0,sizeof(W));
		int n,w;
		scanf("%d %d",&n,&w);
		int i;
		for(i=1; i<=n; i++)
			scanf("%d",&V[i]);
		for(i=1; i<=n; i++)
			scanf("%d",&W[i]);
		int j;
		for(i=1; i<=n; i++)
		{
			for(j=w; j>=W[i]; j--)
			{
				dp[j]=dp[j]>(dp[j-W[i]]+V[i])?dp[j]:(dp[j-W[i]]+V[i]);
			}
		}
		printf("%d\n",dp[w]);
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值