动态规划
定义
Dynamic Programming:
运筹学的一个分支,通常用来解决多阶段决策过程最优化问题。基本思想:将原问题转换为一系列的子问题,然后通过逐层递推来求得最后的解。
动态规划vs.递归
一、递归与动态规划是两个方向刚好相反但是解题思路相同的过程。
两个方法都是通过把一个大的问题化小,小到可以一步解决,递归是采用从顶层层层递归,知道递归出口之后得到结果。
而动态规划是从本来需要递归的最底层开始层层往上计算出结果。
二、动态规划会把递归树中间值存储下来,这样避免重复计算
什么题目使用动态规划做
1.计数
2.最大最小和
3.存在性问题(是否)
方法
1.确定状态
(1)最优策略中的最后一步
(2)子问题:将原问题的规模减少
2.转移方程
通常要定义一维或者二维数组。
3.初始条件&边界情况
4.计算顺序
通常为从小到大,从左上到右下
实例
一、Coin Change
题目:你有三种硬币,分别面值2元,5元和7元,每种硬币都有足够多。买一本书需要27元,如何用最少的硬币组合正好付清,不需要对方找钱。
1.确定状态:
假设最少的硬币组合是k枚。
最后一步:最后一枚硬币(第k枚)是Ak(2/5/7).
子问题:除去最后1枚硬币,需要k-1枚硬币才能达到Ak.
2.转移方程
设f(x)为刚好凑齐x使用的最少硬币数。得到状态方程:
f(x)=min{f(x-2)+1,f(x-5)+1,f(x-7)+1};
3.初始条件:
f(0)=0
边界情况:x为负数或者拼不出x.
当x<0或x=1时,f(x)=Integer.MAX_VALUE;
eg. f(3)=min{f(1)+1,f(-2)+1,f(-4)+1}=Integer.MAX_VALUE;
点击查看代码
public int CoinChange(int[] A,int M){
//2,5,7 //27
int[] f = new int[M+1];
int n =length.A;
int i,j;
f[0]=0;
for(i=1;i<M;++i){
f[i]=Integer.MAX_VALUE;
//Last coin A[j]
//f(i)=min{f(i-A[0])+1,f(x-A[1])+1,f(x-A[2])+1};
for(j=0;j<n;++j){
if(i>=A[j]&&f[i-A[j]]!=Integer.MAX_VALUE){
f(i)=Math.min(f[i-A[j]]+1,f[i]);
}
}
}
if(f[M]==Integer.MAX_VALUE)return -1;//拼不出返回-1
else return f[M];
}
二、Unique Paths
题目:给定m行n列的网格,有一个机器人从左上角(0,0)出发,每一步可以向下或者向右走一步,问有多少种不同的方式走到右下角。
1.确定状态:
假设有k种不同的方式走到右下角。
最后一步:从(m,n-1)或者(m-1,n)到(m,n)
子问题:有a种方式到(m,n-1)+有b种方式到(m-1,n);k=a+b;
2.转移方程
设f(m,n)为给定m行n列的网格,走到右下角的方式。得到状态方程:
f(m,n)=f(m,n-1)+f(m-1,n);
3.初始条件:
f(0,0)=1;
边界情况:m,n为负数
点击查看代码
public int UniquePath(int m,int n){
int[][] f =new int[m][n];
int i,j;
for(i=0;i<m;++i){
for(j=0;j<m;++j){
if(i==0||j==0){f[i][j]=1;}
else {f[i][j]=f[i,j-1]+f[i-1,j];}
}
}
return f[m-1[n-1];
}
三、Jump Game
题目:有n块石头分别在x轴的0,1,...,n-1位置,一只青蛙在石头0,想跳到石头n-1.如果青蛙在第i块石头上,它最多可以向右跳距离ai.问青蛙能否跳到石头n-1.
示例;
input:a=[2,3,1,1,4];output:True;
input:a=[3,2,1,0,4];output:False;
1.确定状态:
假设能跳到。
最后一步:从石头i跳到石头n-1,跳的距离为n-1-i,小于等于ai;
子问题:能从石头0跳到石头i;
2.转移方程
设f(n-1)为一共有n块石头,能从石头0跳到石头n-1。得到状态方程:
f(n-1)=f(i)&&(i+ai>=n-1);
3.初始条件:
n=1:f(0)=TRUE;
点击查看代码
public int JumpGame(int[] a){
int n = A.length;
boolean[] f = new int[n];
f[0] = TRUE;//initialization
int i,j,k;
for(j=1;j<n;++j){
f[j] = FALSE;
for(i=0;i<j;++i){
if(f[i]&&i+a[j]>=j){
f[j]=true;
break;
}
}
}
return f[n-1];
}

浙公网安备 33010602011771号