01背包问题

//二维dp动态规划解决01背包问题(固定重量内尽可能装价值高的物品(物品不可分解)):
//题目见书挑战程序设计竞赛P51的背包问题.

//1.构造dp数组,明确各变量在问题中的实际含义
//分析:首先本题有三个量需要处理:第几个物品,重量,总共能获得的价值
//所以我们构造一个二维数组:dp[i][j],其中i表示可选择的物品数量,j表示重量,dp[i][j]表示总价值
//所以dp[i][j]的总含义就是在第0-i这i+1个物品中,选择总重量不超过j的物品,可获得的最大价值
//关键点:可获得的最大价值,所以每个点虽然都有很多种的取法,但是在这个dp数组中算出来的值,永远
//都是最优解法的值,所以每个dp都只对应一种情况,都是只有一个值
//所以要得到最优解,我们只需要保证起始值的正确性,然后用求最大价值的思路去得到正确的递推式子
//这样起始点正确了,递推方式正确了,那么最终得到的递推结果也就正确了

//2.找出递推的式子,和起始式子的值
//起始式分析:对于本体,如果可选择的物品为0,则dp的值一定为0,所以dp[0][j]=0,当然dp[j][0]=0
//在写代码的时候把dp所有的值初始化为0即可

//递推式子分析:首先分两种情况考虑:(1).这个物品装不下 (2).这个物品装的下
//(1).这个物品装不下:那么只能放弃这个物品,所以转移到下一个物品,且总价值不变,总重量不变
//(2).这个物品装的下:那么这个物品能获得最大价值的可能情况有两种:
//[1].不装这个物品,保持自身的重量和价值和在上一个物品的状态一样,即只转移选择的物品位置(i)
//[2].装这个物品,那么自身的能用的最大重量要减掉这个物品的重量,而总价值可以加上这个物品的价值
//两种方式得到的两个值的最大值即是这一点dp的值

//代码实现:
#include <bits/stdc++.h>
using namespace std;
const int MAX_N=100;
const int MAX_M=10000;
int n,w[MAX_N],v[MAX_N],W;
int dp[MAX_N+1][MAX_M+1];//此处i能达到的最大值为n(共n个物品),所以0-n共n+1个空间要开辟,j同理
int main() {
scanf("%d",&n);
for (int i=0;i<n;i++)
scanf("%d%d",&w[i],&v[i]);
scanf("%d",&W);
for (int i=0;i<n;i++) {//j<n!,因为dp[n][j]由dp[n-1][j]递推而来,没有dp[n+1][j]要递推
for (int j=0;j<=W;j++) {
if (j<w[i]) dp[i+1][j]=dp[i][j];
else dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);
}
}
printf("%d",dp[n][W]);
}

 

//一维dp解决01背包问题(固定重量内尽可能装价值高的物品(物品不可分解)):
//题目见书挑战程序设计竞赛P51的背包问题

//二维dp在解决这些背包问题时,因为开辟了一个二维数组空间,所以一旦物品数量到1e4级以后,基本都会
//存在内存空间不足的问题,所以为了解决这个问题,将二维降到一维,来解决这个问题

//如何降维:二维dp中,我们背包物体的三个量分别是:第几个物品,可用的重量,总共能获得的价值
//其实,在解决背包问题时,我们并不需要算出每个物品能得到的最大价值,我们的限制条件是所能承受
//的重量,所以我们可以去掉"第几个物品"这个量,只保留"可用的重量","总共能获得的价值"这两个量
//这样就实现了将其降维成一维dp数组dp[j]

//处理思路:首先dp的核心递推式子仍然不变:dp[j]=dp[j-w[i]]+v[i],同样我们也是要做两重循环,一重
//循环遍历所有的物品,另一重循环用于求1-W的每个重量对应能得到的最大价值.

//与二维的思路异同点:首先第一重循环都一样,从第一个物品找到最后一个物品
//第二重循环存在差异:此时,我们不需要去考虑dp中那些一个物品都取不了的点(因为这些点的dp值为0)
//所以循环从j=w开始,到j<w[i]时即可结束,其它方面与一维dp基本相同.

//代码实现:
#include <bits/stdc++.h>
using namespace std;
const int MAX_N=100005;
int n,W;
int w[MAX_N],v[MAX_N];
int dp[MAX_N];
int main() {
scanf("%d",&n);
for (int i=0;i<n;i++) scanf("%d%d",&w[i],&v[i]);
scanf("%d",&W);
for (int i=0;i<n;i++) {
//此处注意一定要写成j从W--w[i],而不能反一头来写,这里涉及到能不能重复取一样物品的问题
//这里从W-w[i],说明了j-w[i]中的这些物品都是还没有被第i个物品搜过的,所以这保证了不会
//有多个i物品被搜索(因为后面的可以继承前面的搜索结果,但是前面的不能继承后面的)
for (int j=W;j>=w[i];j--) {//此处为j--,不要写成j++...
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
//注意此处因为剩下的j<w[i]的部分取不了物品,所以价值不会增加,所以就不用继续分析
//但是如果不取的情况下,也有价值获取,则需要将取不了物品的质量做一个循环,把这些不取
//带来的价值也给加上去
}
printf("%d",dp[W]);
return 0;
}}

posted @ 2021-07-20 19:01  jue1e0  阅读(154)  评论(0)    收藏  举报