基本背包问题

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

一、装满背包

装满背包,要求最红认定最大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旅行商问题

原文地址:https://www.cnblogs.com/zhaoyl/p/3741792.html