背包问题(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;
 } 

 

posted @ 2024-01-13 19:39  Syki  阅读(62)  评论(0)    收藏  举报