动态规划背包问题c++

背包问题

01 背包问题

求出限重在j情况下的 可以放入物品的最大价值V, 每个物品只有1个
初始化条件
vector weights = {1,3,5,4,6,4,2};
vector values = {2,4,6,3,1,5,5};

设定 d[i][j] 表示为 (前i个物品在限重j重量下所呈现的,前i个物品不清楚里面具体放了哪几个) 最大价值
设定 d[i - 1][j] 表示为 (前i个物品在限重j重量下所呈现的,前i -1 个物品不清楚里面具体放了哪几个) 最大价值

假如现在的状态是 d[i - 1][j], 已经明确了前i-1个物品所带来的最大价值,
那么第i个物品不放入背包的价值为
d[i][j] = d[i-1][j]
那么第i个物品选择放入背包的价值为
当前物品的价值 + 前i-1个物品在限重 [j - 当前物品重量下的限重] 的最大价值
d[i][j] = value[i]+ dp[i-1][j - wights[i]]

那么状态转移公式应该为 选择两者放入与不放入的中的最大值
d[i][j] = max ( d[i-1][j] , value[i]+ dp[i-1][j - wights[i]])

初始化dp[0][1,2,…j] = 0
我们手动推理写下这个过程

考虑前1个物体在限重0,1,2,3,…j 的 最大价值

d[1][0] = d[0][0] = 0
d[1][1] = max ( d[0][1], 2 + d[0][1 - 1]) = 2
d[1][2] = max ( d[0][2], 2 + d[0][2 - 1 ]) = 2
d[1][3] = max ( d[0][3], 2 + d[0][3 - 1]) = 2
d[1][4] = max ( d[0][4], 2 + d[0][4 - 1]) = 2
d[1][5] = max ( d[0][5], 2 + d[0][5 - 1]) = 2
d[1][6] = max ( d[0][6], 2 + d[0][6 - 1]) = 2

d[2][0] = d[1][0] = 0
d[2][1] = max ( d[1][1], 4 + d[1][1-3]) = 2
d[2][2] = max ( d[1][2], 4 + d[1][2-3]) = 2
d[2][3] = max ( d[1][3], 4 + d[1][0]) = 4
d[2][4] = max ( d[1][4], 4 + d[1][1]) = 6
d[2][5] = max ( d[1][5], 4 + d[1][2]) = 6
d[2][6] = max ( d[1][6], 4 + d[1][3]) = 6

d[3][0] = d[2][0] = 0
d[3][1] = max ( d[2][1] 2, 6 + d[2][1-5]) = 6
d[3][2] = max ( d[2][2] 2, 6 + d[2][2-5]) = 6
d[3][3] = max ( d[2][3] 2, 6 + d[2][3-5]) = 6
d[3][4] = max ( d[2][4] 6, 6 + d[2][4-5]) = 6
d[3][5] = max ( d[2][5] 6, 6 + d[2][0]) = 6
d[3][6] = max ( d[2][6] 6, 6 + d[2][1]) = 8
int package_01(int total_wieght)
{

    // 01 背包问题 求限重j下装入物品的最大价值 每个物品只有一个, 不是无限个
    vector<int> weights = {1,3,5,4,6,4,2};
    vector<int> values =  {2,4,6,3,1,5,5};

   //这里开辟的空间是 NM 也就是 weights.size * total_wieght 的大小
   vector<vector<int>> dp(weights.size() + 1, vector<int>(total_wieght + 1,0));
   for(int i = 1; i < weights.size() + 1; i++)
   {
       for(int j = 1; j <= total_wieght; j++)
       {
           //由于从i 从 1 开始 所以这里的对应值应该减去1
           int current_value = values[i -1];
           int current_weight = weights[i-1];
           if(current_weight <= j)
           {
                dp[i][j] = max(dp[i-1][j], current_value + dp[i-1][j-current_weight]);
           }
           else
           {
                dp[i][j] = dp[i-1][j];
           }
            printf("dp[%d][%d] = %d\n",i,j,dp[i][j]);
       }
   }
   return dp[weights.size()][total_wieght];
}

继续观察这个代码 发现开辟的空间数组大小也可以优化 因为dp[i] 每次循环只依赖上次dp[i-1]的结果
只开辟dp[total_weight +1] 的空间就可以满足计算运行
更新公式如下

dp[j] =max( d[j] , values[i] + dp[3-weights[i]] )
假如这里代表dp[2][j]那么这里代表dp[1][j] 和 dp[1][j-weights[i]]

大限重的value 计算 依赖了小限重的value
那么需要反过来从限重从大往小计算最大价值
j = J ,j-1,j-2,3,2,1

int package_01_small_space(int total_wieght)
{

    // 01 背包问题 求限重j下装入物品的最大价值 每个物品只有一个, 不是无限个
    vector<int> weights = {1,3,5,4,6,4,2};
    vector<int> values =  {2,4,6,3,1,5,5};

   //这里开辟的空间是 NM 也就是 total_wieght + 1 的大小
   int dp[total_wieght + 1] ={0};
   for(int i = 1; i < weights.size() + 1; i++)
   {
       //倒序计算
       for(int j = total_wieght; j >= 1; j--)
       {
           //由于从i 从 1 开始 所以这里的对应值应该减去1
           int current_value = values[i -1];
           int current_weight = weights[i-1];
           if(current_weight <= j)
           {
                // not choose this object
                dp[j] = max(dp[j], current_value + dp[j-current_weight]);
           }
           printf("dp[%d][%d] = %d\n",i,j,dp[j]);
       }
   }
   return dp[total_wieght];
}

完全背包问题

01 完全背包问题 求限重j下装入物品的最大价值 每个物品有无限个 求 最大价值
dp[j] 限重j下的最大价值
dp[j-1] 限重j-1下的最大价值
状态转移方程为
for i in 1,2,3,4,N
dp[j] = max(value[i] + dp[j - weight[i]],)

int package_all(int total_wieght)
{
    // 01 完全背包问题 求限重j下装入物品的最大价值 每个物品有无限个 求 最大价值
    vector<int> weights = {1,3,5,4,6,4,2};
    vector<int> values =  {2,4,6,3,1,5,5};
    
    // 设定 d[i][j] 表示为 前i个物品在j重量下所呈现的最大价值
    /*
        dp[j] 代表限重下的最大价值
        dp[j] = max{value[i]+dp[j - weight[i]],.....}
    
    */
   printf("\n\n");
   int dp[total_wieght + 1] ={0};
   for(int j = 1; j <= total_wieght; j++)
   {
       for(int i = 0; i < weights.size(); i++)
       {
           if(weights[i] <= j)
           {
            dp[j] = max(dp[j] ,values[i] + dp[j - weights[i]]);
           }
       }
     printf("dp[%d] = %d\n",j,dp[j]);
   }

   return dp[total_wieght];
}

多重背包问题

多重背包问题
本质分解为
前i个物品在限重下的最大价值
和本次一次性装入k个物品的最大价值 比较

int package_repeated(int total_wieght)
{
    // 01 多重背包问题 求限重j下装入物品的最大价值 每个物品有N个 求 最大价值
    vector<int> weights = {1,3,5,4,6,4,2};
    vector<int> values =  {2,4,6,3,1,5,5};
    vector<int> counts = {2,1,2,3,1,2,3};
    
    // 设定 d[i][j] 表示为 前i个物品在j重量下所呈现的最大价值
    /*
        k 表示装入 k个 i物品
                  总数量      可以装入的最大数量 两者取最小值
        k < min ( counts[i], j / weights[i] )
    
        dp[i][j]
        转移公式
        装入k个物品 不装入k个物品, 没有第几个

        dp[i][j]
            = max(dp[i-1][j], value[i] * k + dp[i-1][j - k * weight[i]])
    */

   int dp[total_wieght + 1] ={0};
   for(int j = 1; j <= total_wieght; j++)
   {
       for(int i = 0; i < weights.size(); i++)
       {
           for(int k = 0 ; k <= counts[i]; k ++)
           {
               if( k * weights[i] <= j)
               {
                   dp[j] = max(dp[j] ,k * values[i] + dp[j - k * weights[i]]);
               }
           }
       }
     printf("dp[%d] = %d\n",j,dp[j]);
   }

   return dp[total_wieght];
}

恰好装满问题

恰好装满背包
与01 背包区别是初始化过程中 dp[0] = 0 dp[others] = -inf

int package_case(int total_wieght)
{
    // 01 恰好装满背包
    vector<int> weights = {3,5,4,6};
    vector<int> values =  {4,6,6,1};
    vector<int> counts = {1,2,3,1,2,3};
    
    // 设定 d[i][j] 表示为 前i个物品在j重量下所呈现的最大价值
    /*
    dp[0] = [0]
    dp[1] = -inf
    dp[2] = 5
    
    //完全背包
    dp[3] = max(4, 5 + dp[1]) = max(4,5+0) = 5
    //恰好背包
    dp[3] = max(4, 5 + dp[1]) = max(4,5+ -inf) = 4
    */

   vector<int> dp(total_wieght+1,std::numeric_limits<int>::min());
   // 所以对int数组进行非0赋值或初始化时不能用memset()函数,只能循环处理/定义时初始化
   dp[0] = 0;

   for(int j = 1; j <= total_wieght; j++)
   {
       for(int i = 0; i < weights.size(); i++)
       {
           if(weights[i] <= j)
           {
            dp[j] = max(dp[j] ,values[i] + dp[j - weights[i]]);
           }
       }
     printf("dp[%d] = %d\n",j,dp[j]);
   }

   return dp[total_wieght];
}

求方案总数

// 方案总数 求所有方案 台阶问题
https://blog.csdn.net/zhuanzhe117/article/details/72846939

int package_sum(int total_wieght)
{
    // 01 上台阶 上100个台阶的总方案数目
    vector<int> weights = {3,5,4,6,2};
   vector<int> dp(total_wieght+1,0);
   dp[0] = 1;
   /*
   https://blog.csdn.net/zhuanzhe117/article/details/72846939

   dp[j] = sum(dp[j-3],dp[j-5],dp[j-4],dp[j-6],dp[j-2])
   */
   // 所以对int数组进行非0赋值或初始化时不能用memset()函数,只能循环处理/定义时初始化

   for(int j = 1; j <= total_wieght; j++)
   {
       for(int i = 0; i < weights.size(); i++)
       {
           if(weights[i] <= j)
           {
            dp[j] = dp[j] + dp[j - weights[i]];
           }
       }
     printf("dp[%d] = %d\n",j,dp[j]);
   }
   return dp[total_wieght];
}
#include <iostream>
#include <string>
#include <vector>
#include <limits>
using namespace std;
int main(int argc, char *argv[])
{
    //solutions(10);
    // std::vector<int> coins = {1,3,5};
    // std::cout << Solution::change(4,coins);
    printf("argv %s\n",argv[1]);
    int a = atoi(argv[1]);
    int res = package_01_small_space(a);
    printf("\npackage_01_small_space %d\n",res);
    res = package_01(a);
    printf("\npackage_01 %d\n",res);

}

参考链接
https://zhuanlan.zhihu.com/p/93857890
https://blog.csdn.net/zhuanzhe117/article/details/72846939

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值