前言
首先本文参考灯神视频总结,配合视频食用效果更佳哦!动态规划
一、背景
首先了解斐波那契数列,通过斐波那契数列引入动态规划问题,由于递归存在重叠子问题,导致时间复杂度很大,可不可以考虑从递归的条件网上“递归”呢?这样就能够使用之前的条件,达到减少运算的效果,这就是动态规划。(纯个人理解)
二、例题
1.问题:一个人可以选择做下面8个任务,红字代表任务的工资,横轴表示时间段,一个人在一个时间段只能完成一个任务,完成才能离开做下一个。问怎样做任务在0~11时间段收入最高?
2.选和不选
由于0~11时间段,则8个任务都可以考虑,选择做第8个,和不选择做第8个;opt表示考虑做几个的最优解。
prev(i)表示选择第i个后,可以选的上一个,比如prev(8)=5;prev(7)=3
通过以上的分析和计算,可以把问题转化为递归式,如下图所示:
从opt(1)开始往下开始计算可得结果如下图所示:
三、动态规划实战
1.在这串数字当中选出一堆数字,要满足以下条件:
a.两个数不能相邻;
b.可以选多个数字,但每个数只能选一次;
参考上面的选和不选方法:
代码实现:
(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.
给定一个数组,找出和为9的方案。 可以选 多个数字,但每个数字最多只能选一次。
递归写出,肯定要想清楚递归出口情况:
(1)已经处理到一半的时候,已经找到了这种组合了
(2)到递归到最后一个,看最后一个是否等于S;
(3)如果出现arr[i]>S则只考虑不选的情况;
递归式子为:
代码如下:
递归实现:
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];
}