背包问题(01背包)
01背包
- 现有N件物品和一个最多能承重M的背包,第i件物品的重量是$w_i$,价值是$v_i$。在背包能承受的范围内,试问将哪些物品装入背包后,可使价值最大,求这个最大价值。
每件物品只有选和不选两种状态,所以称为01背包问题。
dp[i][j]:在背包承重为j的前提下,从前i个物品中选,能够得到的最大价值。
dp[N][M]即为问题的答案。
- 选第i个物品:第i个物品一定会被选择,所以只需考虑从前i-1个物品中选,且总重量不超过j-w[i]。对应dp[i-1][j-w[i]]+v[i]。
- 不选第i个物品:从前i-1个物品中选,且总重量不超过j。对应dp[i-1][j]。
综上所述,得到递推公式dp[i][j] = max(dp[i-1][j-w[i]]+v[i],dp[i-1][j])
- 由于下标不能为负数,所以要设定前提 j≥w[i]
- 当j<w[i]时,意味着第i个物品不能装入背包,此时dp[i][j]=dp[i-1][j]
dp数组初始化
- 背包承重为0时,装不下任何物品。dp[i][0]=0
- 一个物品也不选,最大价值为0。dp[0][j]=0
所以dp数组初始化为全0。(设置为全局变量,全局变量初始值为0)
优化为一维数组
从dp[i][j]优化到dp[j]
dp[j]的定义:N件物品,背包容量j下的最优解。
枚举背包容量时需要逆序。
- 如果j从小到大顺序枚举,dp[j-w[i]]的下标中,j-w[i]小于j,dp[j-w[i]]已经在第i层循环被计算过了,而真正想要的dp[j-w[i]]应该是第i-1层里面的。j从大到小枚举可以保证dp[j-w[i]]还没有被计算过,也就是第i-1层的数据。
LG2392
复习的最短时间:尽可能用左右脑同时思考,时间差最小的题目量。
假设左脑的思考时间不大于右脑,当复习时间取最短时,左脑的思考时间≤总复习时间/2。
题目可以抽象看作01背包问题,每个学科有s[i]个物体,每个物体的体积为v[j],价值为v[j],背包总体积为总复习时间/2
dp[i][j]:从编号为0到i的物体中选出若干个物体,总体积小于j的最大价值。
dp[i][j]由两种状态转移得到,一种是选第i个物体,一种是不选第i个物体。
代码
#include<iostream> #include<cstring> using namespace std; const int N = 1210; int cnt,t; int s[10],v[30]; int dp[N]; int main() { for(int i=1;i<=4;i++) cin>>s[i]; for(int i=1;i<=4;i++){ memset(dp,0,sizeof dp); //有四个学科,计算每科最短复习时间前要先初始化数组 for(int j=1;j<=s[i];j++){ cin>>v[j]; cnt+=v[j]; } for(int j=1;j<=s[i];j++){ for(int k=cnt/2;k>=v[j];k--){ dp[k]=max(dp[k],dp[k-v[j]]+v[j]); //状态转移方程 } } t+=cnt-dp[cnt/2]; //假设左脑所用时间不大于右脑,dp[cnt/2]表示左脑,cnt-dp[cnt/2]表示右脑,复习该科所需时间不少于右脑花费的时间 cnt=0; } cout<<t<<endl; return 0; }

浙公网安备 33010602011771号