Recursive dynamic programming ---- violent turn expand the number of monetary issues

Then the questions, this question is also used to feel the violence recursion optimization routines into dynamic programming. Try to think of ways of violence, then optimized for dynamic programming. The first is the method of violence to try the original problem. Only try to come up with a method is the most difficult, the most important.

We first look at the title:

To give you an array arr, arr all the values ​​are not repeated is a positive number, and an integer aim. If you can select any number of arr, each number can only be selected once, can cumulatively summed AIM, returns true or false.

Similar ideas subsequence problem string: Let F (i, sum) i is an array index, sum represents the beginning of the array element to the current element and to arr = {3,2,7,13}, aim = 9, for example, the recursive process shown below: (PS: this is an only I can understand FIG, o (╯ □ ╰) o)

So to solve the following steps:

1. write attempt (recursive) version

public static boolean money1(int[] arr, int aim) {
		return process1(arr, 0, 0, aim);
	}

	public static boolean process1(int[] arr, int i, int sum, int aim) {
		if (i == arr.length) {
			return sum == aim;
		}
		//继续往下执行,两种情况:要么选要么不选
		return process1(arr, i + 1, sum, aim) || process1(arr, i + 1, sum + arr[i], aim);
	}

2. analyze whether the conditions for transfer of dynamic programming to meet:

     (1) there are a lot of double counting: the above example is not clear enough, can be replaced {3,2,5,13}, it appears to the third step two f (3,5), a is f (2 5) +0, a +5 obtained by the f (2,0), and f (3,5) follow the same return value affirmative, there is double counting.

     (2) the problem belongs to "no aftereffect" problem: with 1, f (3,5) regardless of which path reaches obtain the return value are the same, it means "no aftereffect."

The review can be converted to dynamic programming.

3.  Analysis of the variable parameters, the value of several variable parameters which represent the state returns, several variable parameters, and their variation range, can be configured several table dimension dp.

Array aim and immutable, i, sum variable.

4. See base case, does not depend on the location listed. (An array { . 3, 2,. 5 }, AIM =. 7, for example, reduce the workload)

Representatives of the two-dimensional table row i, column represents the sum of all the elements and that is (aim in principle not exceed this number, it is clear that if more than the return false).

It is known as the last line of the base case, only when the sum == aim of when is the T, the other is F. So the next available table of contents:

I \ SUM 0 1 2 3 4 5 6 7 8 9 10
0                      
1                      
2                      
3 F F F F F F F T F F F

 

The analysis relies on a common position.

return process1(arr, i + 1, sum, aim) || process1(arr, i + 1, sum + arr[i], aim);

Found, to know the value of a common position, we need to know the value of the next row and the next row and its added value arr [i] to the right column. For example, f (2,0), seen from the above code, dependent on the f (3,0) and f (3,5), so that f (2,0) to F (because f (3,0) and f (3 , 5) are both F).

The review, to know the last line, which in turn will be able to fill out the entire form.

I \ SUM 0 1 2 3 4 5 6 7 8 9 10
0 T F T F T T F T F F F
1 T F T F F T F T F F F
2 F F T F F F F T F F F
3 F F F F F F F T F F F

Overall code is as follows:

package com.gxu.dawnlab_algorithm8;

/**
 * 换钱问题
 * 
 * @author junbin
 *
 *         2019年7月12日
 */
public class Money_Problem {
	public static boolean money1(int[] arr, int aim) {
		return process1(arr, 0, 0, aim);
	}

	public static boolean process1(int[] arr, int i, int sum, int aim) {
		if (sum == aim) { //如果遍历过程中满足该条件,则停止后续遍历
			return true;
		}
		// sum != aim
		if (i == arr.length) { //如果已经到了数组末尾,则直接输出
			return false;
		}
		//继续往下执行,两种情况:要么选要么不选
		return process1(arr, i + 1, sum, aim) || process1(arr, i + 1, sum + arr[i], aim);
	}

	//动态规划
	public static boolean money2(int[] arr, int aim) {
		boolean[][] dp = new boolean[arr.length + 1][aim + 1];//列改为aim+1可以更节省空间
		for (int i = 0; i < dp.length; i++) {
			dp[i][aim] = true;
		}
		for (int i = arr.length - 1; i >= 0; i--) {
			for (int j = aim - 1; j >= 0; j--) {
				dp[i][j] = dp[i + 1][j];
				if (j + arr[i] <= aim) {
					dp[i][j] = dp[i][j] || dp[i + 1][j + arr[i]];
				}
			}
		}
		return dp[0][0];
	}

	public static void main(String[] args) {
		int[] arr = { 1, 4, 8 };
		int aim = 12;
		System.out.println(money1(arr, aim));
		System.out.println(money2(arr, aim));
	}
}

Summary: up to now have experienced two violent turn classic recursive dynamic programming topics, you can feel a high degree of routine, but still feel very skilled, especially in the most difficult to write and try to draw dp table stage version, Keep up!

Published 61 original articles · won praise 9 · views 30000 +

Guess you like

Origin blog.csdn.net/qq_33204444/article/details/95666155