前言

首先本文参考灯神视频总结,配合视频食用效果更佳哦!动态规划

 

一、背景

首先了解斐波那契数列,通过斐波那契数列引入动态规划问题,由于递归存在重叠子问题,导致时间复杂度很大,可不可以考虑从递归的条件网上“递归”呢?这样就能够使用之前的条件,达到减少运算的效果,这就是动态规划。(纯个人理解)

 

二、例题

1.问题:一个人可以选择做下面8个任务,红字代表任务的工资,横轴表示时间段,一个人在一个时间段只能完成一个任务,完成才能离开做下一个。问怎样做任务在0~11时间段收入最高?

java 实现 动态规划窗口位置_递归

 

2.选和不选

由于0~11时间段,则8个任务都可以考虑,选择做第8个,和不选择做第8个;opt表示考虑做几个的最优解。

java 实现 动态规划窗口位置_递归_02

 

 

java 实现 动态规划窗口位置_动态规划_03

 prev(i)表示选择第i个后,可以选的上一个,比如prev(8)=5;prev(7)=3

java 实现 动态规划窗口位置_动态规划_04

通过以上的分析和计算,可以把问题转化为递归式,如下图所示:

java 实现 动态规划窗口位置_递归_05

  从opt(1)开始往下开始计算可得结果如下图所示:

java 实现 动态规划窗口位置_递归_06

 

三、动态规划实战

1.

java 实现 动态规划窗口位置_递归_07

在这串数字当中选出一堆数字,要满足以下条件:

 

a.两个数不能相邻;

b.可以选多个数字,但每个数只能选一次;

参考上面的选和不选方法:

java 实现 动态规划窗口位置_时间段_08

代码实现: 

(1)递归实现

package com.feng.dynamicplanning;

public class DynamicPlanning {
	
	//i表示
	public static int rec_opt(int[] arr, int i) {
		if(0 == i) {
			return 1;
		} else if(1 == i) {
			return 2;
		} else {
			return Math.max(rec_opt(arr, i-2) + arr[i], rec_opt(arr, i-1));
		}
	}
	
	public static void main(String[] args) {
		int[] arr = new int[]{1,2,4,1,7,8,3};
		System.out.println(rec_opt(arr, 6));
	}
}

 时间复杂度:2^n(因为涉及到了重叠子问题) 

(2)非递归实现

public static int dp_opt(int[] arr) {
		int[] opt = new int[arr.length];
		opt[0] = 1;
		opt[1] = Math.max(arr[0], arr[1]);
		for(int i = 2; i < arr.length; i++) {
			opt[i] = Math.max(opt[i-2] + arr[i], opt[i-1]);
		}
		return opt[arr.length-1];
	}

 

2.

java 实现 动态规划窗口位置_java 实现 动态规划窗口位置_09

 给定一个数组,找出和为9的方案。  可以选 多个数字,但每个数字最多只能选一次。

java 实现 动态规划窗口位置_动态规划_10

 

递归写出,肯定要想清楚递归出口情况:

(1)已经处理到一半的时候,已经找到了这种组合了

java 实现 动态规划窗口位置_动态规划java实现_11

(2)到递归到最后一个,看最后一个是否等于S;

(3)如果出现arr[i]>S则只考虑不选的情况; 

递归式子为:

java 实现 动态规划窗口位置_时间段_12

代码如下:

递归实现:

public static boolean rec_subset(int[] arr, int i, int s) {
		if(0 == s) {
			return true;
		} else if(0 == i) {
			return arr[0] == s;
		} else if(arr[i] > s) {
			return rec_subset(arr, i-1, s);
		} else {
			return rec_subset(arr, i-1, s-arr[i]) || rec_subset(arr, i-1, s);
		}
	}

 非递归实现:

public static boolean dp_subset(int[] arr, int S) {
		boolean[][] subset = new boolean[arr.length][S+1];
		//由左到右
		for(int s = 0; s < S+1; s++) {
			subset[0][s] = false;
		}
		if(S >= arr[0]) {
			subset[0][arr[0]] = true;
		}		
		//由上到下
		for(int i = 0; i < arr.length; i++) {
			subset[i][0] = true;
		}
		
		for(int i = 1; i < arr.length; i++) {//行
			for(int s = 1; s < S+1; s++) {//列
				if(arr[i] > s) {
					subset[i][s] = subset[i-1][s];
				} else {
					boolean A = subset[i-1][s-arr[i]];
					boolean B = subset[i-1][s];
					subset[i][s] = A || B;
				}		
			}
		}
		return subset[arr.length-1][S];
	}