Dynamic programming algorithm-solve the classic knapsack problem

1: Introduction to dynamic programming algorithms

  • 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.

  • 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.
    Insert picture description here

  • 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, and further solutions are carried out)

The sub-problems obtained by decomposition are often not independent of each other. The number of different sub-problems is often only of polynomial order. When using the divide-and-conquer method to solve, some sub-problems are repeated many times.

  • Dynamic planning can be gradually advanced by filling in the form to get the optimal solution.

2: Basic steps of dynamic programming algorithm

1: Find out the nature of the optimal solution and characterize its structure.
2: Define the optimal value recursively.
3: Calculate the optimal value from the bottom up.
4: According to the information obtained when calculating the optimal value, construct the optimal solution.

3: Dynamic programming algorithm solves the classic knapsack problem

Backpack problem: There is a backpack with a capacity of 4 pounds. There are the following items
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 loaded items cannot be repeated

Thinking analysis and illustration 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)
. The problem here belongs to the 01 backpack, that is, each item can contain at most one. The infinite backpack can be transformed into a 01 backpack.

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 is the capacity of the backpack. Let v[i][j] denote the maximum value that can be loaded into the backpack with capacity j in the first i items. Then we have the following result:

Insert picture description here
As explained
Insert picture description here
in the figure below, take the above three expressions into the figure
Insert picture description here
above for verification. According to the above analysis, perform the following code demonstration

public class KnapsackProblem {
    
    

	public static void main(String[] args) {
    
    
		// TODO Auto-generated method stub
		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];
		
		//初始化第一行和第一列, 这里在本程序中,可以不去处理,因为默认就是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]]);
					//v[i][j] = Math.max(v[i - 1][j], val[i - 1] + v[i - 1][j - w[i - 1]]);
					//为了记录商品存放到背包的情况,我们不能直接的使用上面的公式,需要使用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
						path[i][j] = 1;
					} else {
    
    
						v[i][j] = v[i - 1][j];
					}
					
				}
			}
		}
		
		//输出一下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();
		}
		
		System.out.println("============================");
		//输出最后我们是放入的哪些商品
		//遍历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 ) {
    
     //从path的最后开始找
			if(path[i][j] == 1) {
    
    
				System.out.printf("第%d个商品放入到背包\n", i); 
				j -= w[i-1]; //w[i-1]
			}
			i--;
		}
		
	}

}

Guess you like

Origin blog.csdn.net/qq_44891295/article/details/105912271