动态规划之0-1背包问题

动态规划之0-1背包问题


1. 问题描述

​ 给定\(n\)件物品和一背包,物品\(i\)的重量是\(w_i\),其价值为\(v_i\),背包的容量为\(c\)。问应如何选择装入背包中的物品,使得装入背包中物品的总价值最大?
​ 在选择装入背包的物品时,对于每种物品\(i\)只有俩种选择,即装入背包或不装入背包。不能将物品\(i\)装入背包多次,也不能只装入部分的物品\(i\),该问题称为0-1背包问题。即价值和重量数组中只能是整数

2.问题分析

​ 该问题具有最优子结构的性质,符合动态规划的前提。主要看该问题的递归关系:
\(k\)表示为第k件物品,\(j\)表示当前背包剩余容量,\(B(k, j)\)表示前\(k\)件物品装入容量为\(j\)的背包时,背包中此时的价值,\(value_k\)表示物品\(k\)的价值,\(weight_k\)表示物品\(k\)的重量

\[B(k, j) = \begin{cases} B(k - j, j) &\text value_k > j \quad ① \\ \max\{B(k - 1, j), B(k - 1, j - w_k) + v_k\} &\text value_k \le j \quad ② \\ \end{cases} \]

​ 即有当决定物品\(k\)是否装入时有以下俩种情况:

  • 当前物品重量大于背包剩余重量时:直接考虑这个物品不加入,继续计算前一个物体加入时的价值

  • 当前物品重量小于等于背包剩余重量时:分别计算第\(k\)件物品加入背包时的价值\(B(k - 1, j - w_k) + v_k\)和第\(k\)件物品不加入背包时的价值\(B(k - 1, j)\),将较大的值赋给当前的\(B(k, j)\)

    解决该问题实际上就是填写下面的表格的过程:

3.代码分析

​ 所用变量:

/**
 * N        表示物品的数目 + 1
 * C        表示背包容量 + 1 
 * weight   存储物品重量数组
 * value    存储物品价值数组
 * B        存储各个时刻的最优解
 **/
#define N 11
#define C 35

int weight[N] = {0, 4, 5, 7, 2, 8, 3, 9, 6, 1, 10};
int value[N] = {0, 25, 14, 15, 4, 14, 5, 14, 8, 1, 10};
int B[N][C] = {0};

​ 核心计算代码:
​ 计算最优解函数:

void knapsack() {
    int i, j;
    for (i = 1;i < N;i++) {
        for (j = 1;j < C;j++) {
            if (weight[i] > j) {
                // 等价于递推式①
                B[i][j] = B[i - 1][j];
            } else {
                // 等价于递推式②
                int value1 = B[i - 1][j - weight[i]] + value[i];
                int value2 = B[i - 1][j];
                B[i][j] = value1 > value2 ? value1 : value2;
            }
        }
    }
}

​ 构造最优解函数:

/**
 * matrix   记录最优解二维数组
 * n        物品数目
 * j        背包容量
 * 
 * 从最优解最后一行,即最后一个物品向前推算
 * 若未加入当前物品和加入当前物品的总价值一样,则表示当前物品不加入背包,即x[i] = 0
 * 否则当前物品需要加入背包(x[i] = 1),且将当前背包容量j修改为加入当前物品后的状态(j -= weight[i])
 **/
void TraceBack(int (*matrix)[C], int n, int j) {
    for (int i = n; i > 0; i--) {
        if (matrix[i][j] == matrix[i - 1][j]) {
            x[i] = 0;
        } else {
            x[i] = 1;
            j -= weight[i];
        }
    }
}

4. 完整代码

/**
 * 0-1背包问题
 * 递推式
 **/
#include "stdio.h"
#include "stdlib.h"

/**
 * N        表示物品的数目 + 1
 * C        表示背包容量 + 1 
 * weight   存储物品重量数组
 * value    存储物品价值数组
 * B        存储各个时刻的最优解
 * x        存储物品选择信息
 **/
#define N 11
#define C 35

int weight[N] = {0, 4, 5, 7, 2, 8, 3, 9, 6, 1, 10};
int value[N] = {0, 25, 14, 15, 4, 14, 5, 14, 8, 1, 10};
// int weight[N] = {0, 9, 3, 4, 5, 2};
// int value[N] = {0, 10, 4, 5, 8, 3};
int B[N][C] = {0};
int article[N] = {0};
int x[N];

void print_matrix(int (*matrix)[C], int i, int j) {
    for (int a = 0;a < i;a++) {
        for (int b = 0;b < j;b++)
            printf("%-4d", matrix[a][b]);
        printf("\n");
    }
}

void knapsack() {
    int i, j;
    for (i = 1;i < N;i++) {
        for (j = 1;j < C;j++) {
            if (weight[i] > j) {
                B[i][j] = B[i - 1][j];
            } else {
                int value1 = B[i - 1][j - weight[i]] + value[i];
                int value2 = B[i - 1][j];
                B[i][j] = value1 > value2 ? value1 : value2;
            }
        }
    }
    print_matrix(B, N, C);
}

void TraceBack(int (*matrix)[C], int n, int j) {
    for (int i = n; i > 0; i--) {
        if (matrix[i][j] == matrix[i - 1][j]) {
            x[i] = 0;
        } else {
            x[i] = 1;
            j -= weight[i];
        }
    }
}

int main(int argc, char const *argv[]) {
    knapsack();
    printf("%d\n", B[N - 1][C- 1]);
    TraceBack(B, N - 1, C - 1);
    for (int i = 1; i < N; i++)
        printf("%d ", x[i]);
    printf("\n");
    system("pause");
    return 0;
}

5.运行结果

​ 其中前面的二维数组为最优解的记录数组B中的值,\(83\)为最优解,下面的一维数组表示物品的是否装入的状态,\(1\)表示装入,\(0\)表示不装入

posted @ 2020-12-15 22:48  Thoughtful_z  阅读(274)  评论(0)    收藏  举报