• 基本背包问题


    本文主要为了记载用,详细解释请自己根据提示亲自画图操作下,切忌眼高手低。

    一、装满背包

    装满背包,要求最红认定最大val值的前提是——此刻背包刚好装满,仅从实现上看,和“可不满”背包的区别只是初始化方法不同。以0-1背包为例,

    V=10,N=3,c[]={3,4,5}, w={4,5,6}

    1)背包不一定装满

          计算顺序是:从右往左,自上而下:因为每个物品只能放一次,前面的体积小的会影响体积大的

    2)背包刚好装满    

          计算顺序是:从右往左,自上而下。注意初始值,其中-inf表示负无穷

    关于装满背包,读者可以自己体会下-inf作用,亲自划一下图,就明白为何如此初始化了。

    参考自《经典背包问题 01背包+完全背包+多重背包

    二、0-1背包

    问题描述:有N件物品和一个容量为V的背包。第i建物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大

    状态方程1:dp[i][j] = max{dp[i - 1][j], dp[i - 1][j - c[i] + w[i]]}

    解决方案1:资料很多,此处从略。

    状态方程2:优化空间,用f[v]代替dp[i][v]:max{f[v], f[v-c[i]] + w[i]}

    for i  in 0 ... N  
        for  v = V ... 0  
            f[v] = max{f[v], f[v-c[i]] + w[i]}  

    至于为什么v是从V到0,自己划一下才能明白。网上说法一大堆,不是自己的理解。

    解决方法2:

    #include <stdio.h>  
    #include <stdlib.h>  
    #include <string.h>  
      
    #define N 1010  
      
    int value[N], volume[N], dp[N];  
      
    // 0-1背包,优化空间  
    void dpPackage(int n, int v)  
    {  
        int i, j;  
      
        memset(dp, 0, sizeof(dp));  
      
        for (i = 1; i <= n; i ++) {  
            for (j = v; j >= volume[i]; j --) {  
                    dp[j] = dp[j] > dp[j - volume[i]] + value[i] ? dp[j] : dp[j - volume[i]] + value[i];  
            }  
        }  
      
        printf("%d
    ", dp[v]);  
    }  
      
    int main(void)  
    {  
        int i, t, n, v;  
      
        scanf("%d", &t);  
      
        while (t --) {  
            // 接收参数  
            scanf("%d %d", &n, &v);  
      
            for (i = 1; i <= n; i ++)    scanf("%d", value + i);  
            for (i = 1; i <= n; i ++)    scanf("%d", volume + i);  
      
            // 0-1背包  
            dpPackage(n, v);  
        }  
      
        return 0;  
    }  
    View Code

    解决方法3(递归):这是最直接的实现动态规划的方法,但往往被人们忽略,摘自《0-1背包问题的递归实现与非递归实现》,源码贴在下面

    #include<iostream>  
    using namespace std;  
      
    const int W = 150;  
    const int number = 5;  
    const int VALUE[] = {60, 20, 10, 60, 100};  
    const int WEIGHT[] = {20, 30, 50, 60, 80};  
       
      
    //function Make( i {处理到第i件物品} , j{剩余的空间为j}) :integer;  
    int Make(int i, int j)  
    {    
        int r1 = 0;  
        int r2 = 0;  
        int r = 0;  
          
        if (i == -1)  
        {  
            return 0;  
        }  
      
        if(j >= WEIGHT[i])   //背包剩余空间可以放下物品 i    
        {  
            r1 = Make(i-1,j - WEIGHT[i]) + VALUE[i]; //第i件物品放入所能得到的价值  
            r2 = Make(i-1,j); //第i件物品不放所能得到的价值    
            r = (r1>r2)?r1:r2;  
        }     
      
        return r;  
    }  
      
      
    void main()  
    {  
        int maxValue = Make(number-1, W);  
        cout<<"maxValue: "<<maxValue<<endl;  
    }  
    View Code

    三、完全背包

    问题描述:有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价格是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大

    状态方程1:dp[i][v] = max{dp[i-1][v - k * c[i]] + k * w[i] | 0 <= k * c[i]<= v} 

    解决方法1:参考《完全背包基本实现

    using namespace std;
     
     
    int maxV[11][201];    
    int weight[11];
    int value[11];
    int V, N;
     
    void main()
    {
        int i, j, k;
        scanf("%d %d",&V, &N);
        for(i = 0; i < N; ++i)
        {
            scanf("%d %d",&weight[i],&value[i]);
        }
        for(i = 0; i < N; ++i)
        {
            for(j = 0; j <= V; ++j)
            {
                if(i > 0)
                {
                    maxV[i][j] = maxV[i-1][j];
                    if(j/weight[i] >= 1)
                    {
                        int max_tmp = 0;
                        for(k = 1; k <= j/weight[i]; ++k)
                        {
                            if(maxV[i-1][j-k*weight[i]] + k*value[i] > max_tmp)
                            {
                                max_tmp = maxV[i-1][j-k*weight[i]] + k*value[i];
                            }
                        }
                        maxV[i][j] = maxV[i][j] > max_tmp ? maxV[i][j] : max_tmp;
                    }
                }else
                {
                    if(j/weight[0] >= 1)
                    {
                        maxV[0][j] = j/weight[0] * value[0];
                    }
                }
            }
        }
        printf("%d",maxV[N-1][V]);
    }
    View Code

    状态方程2:f[v] = max{f[v], f[v-cost] + weight} 这个方案的得到,依赖1)转化为0-1背包问题 2)并压缩空间,参见《01背包、完全背包、多重背包

    for i = 1 ... N  
        for v = 0 ... V  
            f[v] = max{f[v], f[v-cost] + weight}

    解决方法2:

    /*
     * 完全背包问题 
     */  
      
    #include <stdio.h>  
    #include <stdlib.h>  
      
    #define INF 50000000  
      
    typedef struct coin {  
        int price, weight;  
    } coin;  
      
    void dynamicPackage(coin *coins, int n, int v)  
    {  
        if (v < 0) {  
            printf("This is impossible.
    ");  
            return;  
        }  
      
        int i, j, *dp;  
      
        // 动态分配内存  
        dp = (int *)malloc(sizeof(int) * (v + 1));  
      
        // 初始化  
        dp[0] = 0;  
        for (i = 1; i <= v; i ++)    dp[i] = INF;  
      
        // 完全背包问题  
        for (i = 1; i <= n; i ++) {  
            for (j = coins[i].weight; j <= v; j ++) {  
                dp[j] = (dp[j] < dp[j - coins[i].weight] + coins[i].price) ? dp[j] : dp[j - coins[i].weight] + coins[i].price;  
            }  
        }  
      
        if (dp[v] >= INF)  
            printf("This is impossible.
    ");  
        else  
            printf("The minimum amount of money in the piggy-bank is %d.
    ", dp[v]);  
      
      
        // 清理内存  
        free(dp);  
        dp = NULL;  
    }  
      
      
    int main(void)  
    {  
        int t, e, f, n, i;  
        coin *coins;  
      
        scanf("%d", &t);  
      
        while (t --) {  
            scanf("%d %d", &e, &f);  
            scanf("%d", &n);  
      
            // 接收货币  
            coins = (coin *)malloc(sizeof(coin) * (n + 1));  
            if (coins == NULL)  exit(-1);  
      
            for (i = 1; i <= n; i ++) {  
                scanf("%d %d", &coins[i].price, &coins[i].weight);  
            }  
      
            // 完全背包  
            dynamicPackage(coins, n, f - e);  
      
      
            free(coins);  
            coins = NULL;     
        }     
      
        return 0;  
    }  
    View Code

    四、0-1背包 和 完全背包 装满方案数

    先说下,问题的意义,

    1)完全背包装满

    假设现在有1元、2元、5元的纸币很多张,现在需要20块钱,你能给多少种找钱方案,这就可以认为是完全背包问题,即背包容量为20,物品体积分别为1、2、5。

    2)01背包装满

    给定一个数m,将m拆成不同的自然数的和的形式有多少种方案,这就是典型的01背包问题,背包容量为m,物品件数为k,这里面的k是隐含条件,可以求出来,因为m最多由1+2+…+k得到,由此可以根据m求得物品件数的上限。此题非递归见下文“背包问题”,递归方法见

    解题笔记(31)——从数列1,2...n中随意取几个数,使其和等于m

    具体解决方法,这里不再陈述,参见《背包问题——“01背包”及“完全背包”装满背包的方案总数分析及实现

    此题也可以先求出小于m的所有可能的数的组合方案,然后从中找出方案内数字和等于m的。具体可参考《解题笔记(21)——字符串的排列组合问题

    另外,对于可以不满的情况,有如下博文分析方案总数,

    背包问题——“完全背包”最优方案总数分析实现

    背包问题——“01背包”最优方案总数分析及实现

    五、多重背包

    待续。

    六、动态规划之《TSP旅行商问题

  • 相关阅读:
    Apache部署Django项目
    Docker
    常用算法
    Go之基本数据类型
    Go之流程控制
    Go基本使用
    Go安装与Goland破解永久版
    Linux
    详解java中的byte类型
    Linux统计文本中某个字符串出现的次数
  • 原文地址:https://www.cnblogs.com/zhaoyl/p/3741792.html
Copyright © 2020-2023  润新知