动态规划[6]-多重背包问题

2018/03/15 刷题 动态规划 多重背包问题
  本文为「原创」内容,如需转载请注明出处!             
本文共 3692 字,需 46 分钟阅读

问题

有 N 种物品和一个容量为 V 的背包。第 i 种物品最多有 n[i] 件可用,每件价值是 p[i],重量是 w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

注:该问题与完全背包一样,只是数量限制的区别,一个限量,一个不限量

思路

  1. 状态定义:
     v(i,j) 表示容量为 j,物品个数为 i 的最大价值
     k[i], p[i], w[i] 分别表示每件物品的数量、价值、重量
    
  2. 状态转移:
    \(V(i,j) = \begin{cases} 0 & i=0 或 j=0 \\ V(i-1,j) & j < w(i) \\ max \begin{cases} V(i-1,j-w(i) \times k(i)) + p(i) \times k(i) \\ V(i-1,j) \\ \end{cases} & j \geq w(i) \times k(i) 与 k(i) \leq n(i) \end{cases}\)

实现

/**
 * 多重背包问题
 *
 * @author ychost
 * @date 2018-3-16
 */
public class KnapsackMultiple {
    /**
     * 与完全背包相比较就是每种物品的数量是有限的且是指定的数量
     *
     * @param weights  物品的重量
     * @param prices   物品的价值
     * @param numbers  物品的数量
     * @param capacity 背包容量
     * @return 背包所装物品的最大价值
     */
    static int getMaxPrice(int[] weights, int[] prices, int[] numbers, int capacity) {
        int count = weights.length;
        int[][] dp = new int[count + 1][capacity + 1];
        for (int i = 1; i <= count; i++) {
            int w = weights[i - 1];
            int p = prices[i - 1];
            int n = numbers[i - 1];
            for (int j = 1; j <= capacity; j++) {
                //只有这里的循环终止条件比完全背包多了一个数量限制
                for (int k = 0; k * w <= j && k <= n; k++) {
                    //一般为 k=0 的时候,物品重量超过容量
                    if (j < w) {
                        dp[i][j] = dp[i - 1][j];
                        //比较放下物品和不放物品的价值谁大
                    } else {
                        int before = dp[i - 1][j];
                        int after = dp[i - 1][j - k * w] + p * k;
                        dp[i][j] = before > after ? before : after;
                    }
                }
            }
        }
        print(dp, weights, prices, numbers, capacity);
        return dp[count][capacity];
    }

    /**
     * 打印每个物品的数量取值,从第1个物品到最后一个依次打印
     *
     * @param dp
     * @param weights
     * @param prices
     * @param numbers
     * @param capacity
     */
    static void print(int[][] dp, int weights[], int[] prices, int numbers[], int capacity) {
        int j = capacity;
        int i = weights.length;
        Stack<String> stack = new Stack<>();
        boolean isError;
        while (i > 0 || j > 0) {
            isError = true;
            int w = weights[i - 1];
            int p = prices[i - 1];
            int n = numbers[i - 1];
            if (i > 0 && j > 0) {
                for (int k = 0; k * w <= capacity && k <= n; k++) {
                    if (dp[i][j] == dp[i - 1][j]) {
                        stack.push(String.valueOf(0));
                        --i;
                        isError = false;
                        break;
                    } else if (dp[i][j] == dp[i - 1][j - w * k] + k * p) {
                        stack.push(String.valueOf(k));
                        j -= w * k;
                        --i;
                        isError = false;
                        break;
                    }
                }
                if (isError) {
                    throw new RuntimeException("dp 数据有误");
                }
            } else if (j == 0) {
                stack.push(String.valueOf(0));
                --i;
            } else if (i == 0) {
                break;
            }
        }
        Collections.reverse(stack);
        String str = "{" + String.join(",", stack) + "}";
        System.out.println(str);
    }
}

测试数据


public class KnapsackMultipleTest {

    @Test
    public void getMaxPrice() {
        int[] weights = {3, 4, 5, 6, 7};
        int[] prices = {5, 6, 9, 10, 11};
        int[] numbers = {1, 5, 1, 5, 5};
        int capacity = 13;
        System.out.println(KnapsackMultiple.getMaxPrice(weights,prices,numbers,capacity));
    }
}
//output
//{0,2,1,0,0}
//21

搜索

    文章目录