哪些题可以用动态规划来做?
- 计数
- 有多少种方式走到右下角
- 有多少种方法选出k个数使得和是Sum
- 求最大最小值
- 从左上角走到右下角路径的最大数字和
- 最长上升序列长度
- 求存在性
- 取石子游戏,先手是否必胜
- 能不能选出k个数使得和是Sum
DP算法
动态规划的意义就是通过采用递推(或者分而治之)的策略,
通过解决大问题的子问题从而解决整体的做法。
动态规划的核心思想是巧妙的将问题拆分成多个子问题,通过计算子问题而得到整体问题的解。
而子问题又可以拆分成更多的子问题,从而用类似递推迭代的方法解决要求的问题。
动态规划组成部分
- 确定状态
- 转移方程
- 初始条件(f(0)时)和边界情况(数组越界)
解决步骤
- 确定状态
- 解动态规划的时候需要开一个数组,数组的每个元素f[i] 或f[i][j] 代表什么
- 两个意识:
- 最后一步
- 子问题
Coin Change
- 三种硬币 分别面值2元,5元 和7元 每种硬币都足够多
- 买一本书需要27元
- 如何用最少的硬币组合正好付清,不需要对方找钱
设状态:
f(k) = 最少用多少枚硬币拼出k元
所以 :
f(27) = min{ f(27-2)+1,f(27-5)+1,f(27-7)+1 }
递归解法
int f(int X){
if(X==0) return 0;
int res = Integer.MAX_VALUE;
if(X>=2) res = Math.min(f(X-2)+1,res);
if(X>=5) res = Math.min(f(X-5)+1,res);
if(X>=7) res = Math.min(f(X-7)+1,res);
return res;
}
// 最优策略为k枚硬币
// dp[n] 为k枚硬币中第n个的面值
// 27 = dp[n] +dp[n-1]
dp解法
// {2,5,7} 27
public static int DP06(int[] arr,int X){
确定状态 f[i] : 最少用f[i] 种方法凑够i元
int dp[] = new int[X+1];
dp[0] = 0;
int n = arr.length;
转移方程 f(27) = min{ f(27-2)+1,f(27-5)+1,f(27-7)+1 }
int i ,j;
for (j = 0; j < X; j++) {
dp[j] = Integer.MAX_VALUE;
for (i = 0; i < n; i++) {
// dp[A] A可能为负
// MAX_VALUE +1 结果在计算机里面为负
if (j>arr[i]&&dp[j]!=Integer.MAX_VALUE){
dp[j] = Math.min(dp[j-arr[i]],dp[j] );
}
}
}
if (dp[X] == Integer.MAX_VALUE) return -1;
return dp[X];
}
Fibonicci 递归
递归思想 Fibonicci数列
```
public static int Fibonacci(int n){
// 递归法 复杂度O(2^n)
if (n==1) return 1;
if (n==2) return 2;
return Fibonacci(n-1)+Fibonacci(n-2);
}
```
DP策略 Fibonicci数列 将计算过的f(n)保存起来
```
private static int[] arr = new int[50];
public static int DP02(int n){
if (n<=2) return 1;
if (arr[n]!=0) return arr[n];
else {
arr[n] = Fibonacci(arr[n-1])+Fibonacci(n-2);
return arr[n];
}
//arr数组初始化为0,arr[i]就表示f(i),
// 每次先判断arr[i]是否为0,如果为0则表示未计算过,则递归计算,
// 如果不为0,则表示已经计算过,那就直接返回。
```
机器人走格子
/*
* 机器人走格子 只能向下或者向右走一步
* 问有多少种不同的方法可以到达最右下角的格子
* D X X X X
* X X X X X
* X X X X P
*
* 从D(1,1)走到P
* */
*
/*
* 思路
* i-1,j 向下 i,j-1 向右 为状态转移方程
* */
k1 l1 dp[0][1]+d[1][0] =1+1 = dp[1][1] = 2
k1 l2 02 + 11 = __ 02 应该是2步走到的 所以边界条件都需要设定初始值 like dp[0][index] dp[index][0]
k1 l3 03 + 12 = __
public static int DP03(int i,int j){
int[][] dp = new int[i][j];
dp[0][0] = 0;
// 初始化边界
for(int k = 0; k < i; k++) {
dp[0][k] = 1;
}
for (int k = 0; k < i; k++) {
dp[k][0] = 1;
}
for (int k = 1; k <= i; k++){
for (int l = 1; l <= j; l++) {
dp[k][l] = dp[k-1][l]+dp[k][l-1];
}
}
return dp[i][j];
}
小青蛙跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级,求该青蛙跳上一个n级的台阶共有多少种跳法
递归与分治策略
递归算法
DP思想
先找状态方程:
f(n) : 调到n处的跳法种数
f(n) = f(n-1) +f(n-2)
public int PD04(int n){
int[] dp = new int[n];
if(n<=2) return n;
else{
if(dp[n]!=0) return dp[n-1]+dp[n-2];
return dp[n];
}
}
最长子序列
https://blog.csdn.net/hrn1216/article/details/51534607
https://blog.csdn.net/weixin_34476847/article/details/114030706