Dynamic Programming
DP定义:动态规划是分治的延伸,大事化小,小事化了。
三个特点:把原来的问题分成几个相似的问题;所有问题只要一次就可以解决存储子问题的解
**四要素:**状态定义 状态间的转移方程 状态的初始化 返回的结果
使用的场景: 最值,可不可行 是不是 方案的个数
第一题 Fibonacci
斐波那契数
斐波那契数
public class Solution {
public int Fibonacci(int n) {
if(n==0){
return 0;
}
if(n==1||n==2){
return 1;
}
return Fibonacci(n-1)+Fibonacci(n-2);
}
} //递归的方式
数列状态 :数列的第i项的值
转移方程 : F(i):F(i-1)+F(i-2)
初始状态:需要前两个的值 F(0)=0 F(1)=1
返回F(n)
int fn2=0;
int fn1=1;
int fn=0;
if(n<=1){
return n;
}
for(int i=2;i<=n;++i){
fn=fn1+fn2;
fn2=fn1;
fn1=fn;
}
return fn;
第二题 变态青蛙跳台阶
变态青蛙跳台阶
一只青蛙可以跳两级台阶,也可以跳一阶台阶,…也可以一次跳上n级台阶 问跳 上n级台阶有多少种跳法
状态F(i):跳上i级台阶的方法个数
转移方程:F(i) :F(i-1)+F(i-2)+F(i-3)…+F(0)
F(i-1): F(i-2)+F(i-3)+…+F(0)
F(i): F(i-1)+ F(i-1)==2 F(i-1)
初始状态:F(1)
返回 F(n) =2F(1)
public int jumpFloorII(int target) {
if(target==0){
return 0;
}
int ret=1;
for(int i=2;i<=target;++i){
ret *=2; //此处也可以使用移位操作
}
return ret;
}
第三题 青蛙跳台阶(如同斐波那契数)
青蛙只能跳一级台阶或两级台阶问跳上第n级台阶跳法
状态F(i):跳上i级台阶的方法个数
转移方程:F(i) :F(i-1)+F(i-2)
初始状态:F(1)=1; F(2)=2;
返回 F(n)
public int jumpFloor(int target) {
int fn2=1;
int fn1=2;
int fn=0;
if(target<=2){
return target;
}
for(int i=3;i<=target;++i){
fn=fn1+fn2;
fn2=fn1;
fn1=fn;
}
return fn;
}
第四题 矩形覆盖 ( 如同斐波那契数)
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
状态F(i):2*n大矩形被覆盖的方法个数
转移方程:F(i) :F(i-1)+F(i-2)
初始状态:F(1)=1; F(2)=2;
返回 F(n)
public int rectCover(int target) {
int fn2=1;
int fn1=2;
int fn=0;
if(target<=2){
return target;
}
for(int i=3;i<=target;++i){
fn=fn1+fn2;
fn2=fn1;
fn1=fn;
}
return fn;
}
第五题 最大连续子数组和
例如{6,-3,-2,7,-15,1,2,2} 连续子向量和最大为8(数组从0到3下标)
问题; 数组最大连续和
子问题: 局部元素构成数组,最大连续和
状态F(i):前i个元素组成数组,最大连续和
转移方程: F(i): F(i-1)
此时 无法体现前i个元素中 子序列是连续的 所以无效
状态:以第i个元素结尾最大连续子序列和
转移方程: F(i)=max(F(i-1)+a[i],a[i])
初始状态:F(0)=a[0];
返回值: max(F(i))
public int FindGreatestSumOfSubArray(int[] array) {
int ret=array[0];
for(int i =1;i<array.length;i++){
array[i]=Math.max(array[i-1]+array[i],array[i]);
ret=Math.max(ret,array[i]);
}
return ret;
}
第六题 字符串分割
给定s=“nowcode”;
dict=[“now”, “code”].
返回true,因为"nowcode"可以被分割成"now code".
问题:单词能否成功分割
子问题:单词的部分字母能否可以成功分割
状态:单词前i个字符能否成功分割
转移方程:F(i) j<i&&F(j)&&[j+1,i]能否可以在字典中找到
判断前i个字符是否在词典中也可以找到
返回:F(s.length)
import java.util.*;
public class Solution {
public boolean wordBreak(String s, Set<String> dict) {
if(s.length()==0){
return false;
}
boolean[] canbreak=new boolean[s.length()+1];
for(int i=1;i<=s.length();i++){
//整体
if(dict.contains(s.substring(0,i))){
canbreak[i]=true;
continue;
}
//F(i) j<i&&F(j)&&[j+1,i]
for(int j=i-1;j>0;j--){
if(canbreak[j]&&dict.contains(s.substring(j,i))){
canbreak[i]=true;
break;
}
}
}
return canbreak[s.length()];
}
}