- 典型动态规划问题
- 重叠子问题
- 最优子结构
package dynamic_programming;
import javax.security.auth.login.AccountException;
/**
* @program: Aglorithm
* @Date: *****
* @Author: Kyrie
* @Description:
* 以斐波那契数列作为引子,引入动态规划
* 斐波那契数列:Fib(n) = Fib(n-1) + Fib(n-2); n > 2
* Fib(n) = 1; 0 < n <= 2;
*
* 分析:
* 常规的递归方式:
* 如Fib(6) = Fib(5) + Fib(4);
* Fib(5) = Fib(4) + Fib(3); Fib(4) = Fib(3) + Fib(2);
* ....
* Fib(3) = Fib(2) + Fib(1);
* Fib(2) = 1;
* Fib(1) = 1;
* 这种方式会重复计算子问题,比如Fib(3),一共计算了3次;Fib(2)重复计算了5次,效率低。
* 动态规划:
* 1.自顶向下的备忘录法
* 2.自底向上的动态规划
*/
public class Dp01_Fib {
//常规方式
public static int Fib(int n){
if(n <= 0)
return 0;
if(n <= 2)
return 1;
else
return Fib(n-1) + Fib(n-2);
}
//改成动态规划
public static int DpFib(int n){
//用一个数组记录已经计算的值
int[] values = new int[n+1]; //初始化为0
return fib(n, values);
}
public static int fib(int n, int[] value){
if(value[n] != 0) //value[n]代表第n个数的值,若已经计算过,则直接返会
return value[n];
//否则,计算并记录下其值
if(n <= 2)
return 1;
else
return fib(n-1,value) + fib(n-2,value);
}
/*
求解金矿问题
动态规划--最优化
贪心算法--prim算法
*/
//递归版本
public static int cal(int[] p, int len){
if(len == 0){
return 0;
}
int q = Integer.MIN_VALUE;
for (int i = 1; i <= len; i++) {
q = Math.max(q, p[i-1] + cal(p, len-i)); //p[0]表示取了一段,p[n-1]表示取了一整段
}
return q;
}
//备忘录版本-记录算过的值,已经算过的直接返回
public static int cal2(int[] p, int len){
int[] memo = new int[len + 1];
return cut(p, len, memo);
}
public static int cut(int[] p, int len, int[] memo){
if(memo[len] != 0)
return memo[len];
int q = -1;
if(len == 0){
q = 0;
}
for (int i = 1; i <= len; i++) {
q = Math.max(q, p[i-1]+cut(p, len-i, memo)); //%计算每次切割的最大收益
}
memo[len] = q;
return q;
}
public static int cutMemo(int []p, int len)
{
int[] r = new int[len+1];
for(int i = 0;i <= len; i++)
r[i] = -1;
return cut2(p, len, r);
}
public static int cut2(int[] p, int n, int[] r)
{
int q = -1;
if(r[n] >= 0)
return r[n];
if(n == 0)
q = 0; //没有直接返回,而是保存下来
else {
//整个for循环就是找出长度为N(1,2,3,4,...)的钢条的最大收益值
for(int i = 1;i <= n;i++)
q=Math.max(q, cut2(p, n-i, r) + p[i-1]);
//System.out.println(q);
}
r[n] = q; //剩余长度的最大值,保存长度为N(1,2,3,4,...)的钢条的最大收益值
//System.out.println(q);
//比如,如果要求长度为4的钢条的最大收益,那么,可能被切割成1-3,1-1-2,1-2-1,...,等
//但是,最大收益却是2-2的切割方式,其中前面1-1-2的切割方式中已经计算了长度为2的最大收益
//所以遇到了,就直接返回了
return q;
}
//自底向上的动态规划
public static int dp_cut(int[] p, int len){
//创建并初始化状态表
int[] arr = new int[len+1]; //收益的最大值,保存长度为N(1,2,3,4,...)的钢条的最大收益值
for (int i = 1; i <= len ; i++) {
int q = -1;
for (int j = 1; j <= i; j++) {
q = Math.max(q, arr[i-j] + p[j-1]);
}
arr[i] = q;
}
return arr[len];
}
public static void main(String[] args) {
int[] p = {1, 5, 8, 9, 10, 17, 17, 20, 24, 30}; //钢条价格,len为钢条的长度
//System.out.println(cal(p,4)); //求最大收益
System.out.println(cutMemo(p, 4));
System.out.println(cal2(p, 4));
System.out.println(dp_cut(p, 4));
}
}
-
最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。-
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
-
class Solution {
//思路:若sum小于0则舍弃,保留当前数为sum,反之加上当前数,
//判断sum与之前保存的结果比较,保留大的一方。最终返回结果
public int maxSubArray(int[] nums) {
int sum = 0;
int res = nums[0];
for(int num:nums){
sum = sum > 0 ? sum + num : num;
if(res < sum){
res = sum;
}
}
return res;
}
}
- 改成动态规划
package dynamic_programming;
/**
* @program: Aglorithm
* @Date: ****
* @Author: Kyrie
* @Description:
* [-2,1,-3,4,-1,2,1,-5,4]
* 求最大子序和
* 6 ->[4,-1,2,1]
* 动态规划求解:
* 分析:
* 定义数组dp[i]为当前状态的最优的值,定义head指针指向子序列数组的头部,
* 其中dp[i]中的最大值对应的索引为子序列数组的尾部。
* 若当前数与上一次的最优解之和大于当前数,即dp[i-1]+arr[i] > arr[i],则arr[i]保留,head指针不变
* 或者 dp[i-1] > 0, head不移动;
* dp[i] = max(dp[i-1] + arr[i], arr[i]),若dp[i] = arr[i],则head = i;
* i arr[i] dp[i] head
* 0 -2 -2 0
* 1 1 1 1
* 2 -3 max(-2,-3) 1
* 3 4 max(-2,4) 3
* 4 -1 max(4-1,-1) 3
* ...
* 6 1 6 3 -> 最大子序:head -> i
* 7 -5 1 3
* 8 4 5 3
*
*/
public class Dp02_SumSeq {
public static int maxSubArray(int[] arr) {
int[] dp = new int[arr.length];
dp[0] = arr[0];
//int head = 0;
//int tail = 0;
for (int i = 1; i < arr.length; i++) {
dp[i] = Math.max(dp[i-1]+arr[i], arr[i]);
}
int max = dp[0];
for (int i = 0; i < dp.length; i++) {
max = dp[i] > max ? dp[i] : max;
//tail = i;
}
return max;
}
public static void main(String[] args) {
int[] arr = {-2,1,-3,4,-1,2,1,-5,4};
System.out.println(maxSubArray(arr));
}
}
执行用时 : 3 ms, 在Maximum Subarray的Java提交中击败了66.01% 的用户
内存消耗 : 38.5 MB, 在Maximum Subarray的Java提交中击败了84.41% 的用户