Java programming: dynamic programming

Application scenario-backpack problem

Backpack problem: There is a backpack with a capacity of 4 pounds. The following items are available
Insert picture description here

  1. The required goal is that the total value of the loaded backpack is the largest, and the weight does not exceed
  2. The required items cannot be repeated

Introduction to dynamic programming algorithms

  1. The core idea of ​​the dynamic programming (Dynamic Programming) algorithm is: divide the big problem into small problems to solve, so as to obtain the optimal solution step by step.
  2. The dynamic programming algorithm is similar to the divide-and-conquer algorithm. Its basic idea is to decompose the problem to be solved into several sub-problems, first solve the sub-problems, and then obtain the solution of the original problem from the solutions of these sub-problems.
  3. Unlike the divide-and-conquer method, which is suitable for problems solved by dynamic programming, the sub-problems obtained by decomposition are often not independent of each other. (That is, the solution of the next sub-stage is based on the solution of the previous sub-stage for further solutions)
  4. Dynamic planning can be gradually advanced by filling in the form to get the optimal solution.

Idea analysis and illustration

  1. The backpack problem mainly refers to a backpack with a given capacity and a number of items with a certain value and weight. How to choose items to put in the backpack to maximize the value of the items. Among them, it is divided into 01 backpack and complete backpack (a complete backpack refers to: each item has unlimited pieces available)
  2. The problem here belongs to the 01 backpack, that is, at most one item can be placed. The infinite backpack can be transformed into a 01 backpack.
  3. The main idea of ​​the algorithm is solved by dynamic programming. For the i-th item that is traversed each time, determine whether the item needs to be put into the backpack according to w[i] and v[i]. That is, for a given n items, let v[i] and w[i] be the value and weight of the i-th item, respectively, and C be the capacity of the backpack. Let v[i][j] denote the maximum value that can be loaded into the backpack of capacity j in the first i items. Then we have the following results:
    (1) v[i][0]=v[0][j]=0; //It means that the first row and the first column of the table are 0
    (2) When w[i ]> j: v[i][j]=v[i-1][j] // When the capacity of the new product to be added is greater than the capacity of the current backpack, the loading of the previous cell is used directly Strategy
    (3) When j>=w[i]: v[i][j]=max{v[i-1][j], v[i]+v[i-1][jw[i] ]}
    // When the capacity of the new product to be added is less than or equal to the capacity of the current backpack,
    // the method of loading:
    v[i-1][j]: is the maximum value
    v of the previous cell [i]: Indicates the value of the current product
    v[i-1][jw[i]]: Load i-1 product to the maximum value of the remaining space jw[i]
    when j>=w[i]: v [i][j]=max{v[i-1][j], v[i]+v[i-1][jw[i]]}:

Insert picture description here

Code

package dynamic;

public class KnapsackProblem {
    
    
    public static void main(String[] args) {
    
    
        int[] w = {
    
    1, 4, 3};  // 物品的重量
        int[] val = {
    
    1500, 3000, 2000};  // 物品的价值;这里的val[i] 就是v[i]
        int m = 4;  // 背包的容量
        int n = val.length; // 物品的个数

        // 创建二维数组
        // v[i][j] 表示前i个物品中能够装入容量为j的背包中的最大价值
        int[][] v = new int[n + 1][m + 1];
        // 为了记录放入商品的情况,定义一个二维数组
        int[][] path = new int[n + 1][m + 1];


        // 1. 初始化第一行第一列,这里在本程序中可以不处理(默认就是0)
        for (int i = 0; i < v.length; i++) {
    
    
            v[i][0] = 0;    // 将第一列设置为0
        }
        for (int i = 0; i < v[0].length; i++) {
    
    
            v[0][i] = 0;    // 将第一行设置为0
        }

        // 根据得到的公式来动态规划处理
        for (int i = 1; i < v.length; i++) {
    
       // 不处理第一行 i是从1开始的
            for (int j = 1; j < v[0].length; j++) {
    
     // 不处理第一列 j是从1开始的
                // 公式
                if (w[i - 1] > j) {
    
     // 因为程序i从1开始,因此原来公式中w[i] 修改为w [i-1]
                    v[i][j] = v[i - 1][j];
                } else {
    
     // 因为i从1开始,因此公式需要调整为以下代码
                    //v[i][j] = Math.max(v[i - 1][j], (val[i - 1] + v[i - 1][j - w[i - 1]]));
                    // 为了记录商品存放到背包的情况,不能简单地使用max公式,需要使用if-else-来体现公式
                    if (v[i - 1][j] < (val[i - 1] + v[i - 1][j - w[i - 1]])) {
    
    
                        v[i][j] = val[i - 1] + v[i - 1][j - w[i - 1]];
                        // 记录最优情况
                        path[i][j] = 1;
                    } else {
    
    
                        v[i][j] = v[i - 1][j];
                    }
                }
            }
        }

        // 输出一下v看一下目前的情况
        printArrTwo(v);

        // 输出最后放入的哪些商品
        // 遍历path,会输出所有的放入情况,其实我们只需要最后一个
        /*for (int i = 0; i < path.length; i++) {
            for (int j = 0; j < path[i].length; j++) {
                if(path[i][j]==1){
                    System.out.printf("第%d个商品放背包\n",i);
                }
            }
        }*/
        int i = path.length - 1;
        int j = path[0].length - 1;
        while (i > 0 && j > 0) {
    
     //从pth数组最后开始找
            if (path[i][j] == 1) {
    
    
                System.out.printf("第%d个商品放背包\n", i);
                j -= w[i - 1];
            }
            i--;
        }
    }

    /**
     * 遍历二维数组
     *
     * @param v 二维数组
     */
    public static void printArrTwo(int[][] v) {
    
    
        for (int i = 0; i < v.length; i++) {
    
    
            for (int j = 0; j < v[i].length; j++) {
    
    
                System.out.print(v[i][j] + " ");
            }
            System.out.println();
        }
    }
}

Guess you like

Origin blog.csdn.net/KaiSarH/article/details/109066741