动态规划
-
什么情况下可以用到动态规划问题(求所有,不用)
1、求最大值/最小值(从左上角到右上角路径的最大数字和、最长上升子序列长度)
2、求方案数(有多少种方式走到右下角、有多少种方法选出K个数使得和是Sum)
3、求存在性(取石子游戏,先手是否必胜;能不能选出K个数使得和是Sum) -
动态规划四步解题法
1、确定状态
2、状态方程
3、初始条件和边界情况
4、计算顺序 -
举例说明解法
你有三种硬币,分别面值2元,5元和7元,每种硬币都有足够多。买一本书需要27元。如何用最少的硬币组合正好付清,不需要对方找钱?
确定状态:
1、先看最后一步
最后一步:存在最后一枚硬币,an(2、5、7)
不去关心前面的a1、a2…an-1,只确定前面拼出了27-an
2、分解为子问题
如果an是2,f(27)应该是:f(27-2)+1
如果an是5,f(27)应该是:f(27-5)+1
如果an是7,f(27)应该是:f(27-7)+1
状态方程:
对于任意x:f[x]=min{ f[x-2]+1,f[x-5]+1,f[x-7]+1}
初始条件和边界情况:
初始条件:设定f(0)=0
边界情况:如果不能组合出Y,设定f(Y)=正无穷
dp[i]=Integer.MAX_VALUE;
if (i-coins[j]>=0&&
//钱数大于硬币数值
dp[i-coins[j]]!=Integer.MAX_VALUE&&
//之前子问题没有最优解
dp[i-coins[j]]+1<dp[i])
//下一个金额硬币数量大于上一个
举例:1、f(1)=f(1-2)+1=f(-1)+1=正无穷
2、f(2)=f(2-2)+1=f(0)+1=1
计算顺序:
可能要考虑,从f(27)、f(26)还是f(1)、f(2)开始呢?
原则:f(x)=f(x-1)+f(x-2)+…确保,等式右边都是已知的,计算过的。如果计算f(5)的时候,发现f(3)还没有计算,那就是错的。
for (int i = 1; i <dp.length; i++) {
for (int j = 0; j < n; j++) {
....
}
}
原题:
public static void main(String[] args) {
int[] coins=new int[]{2,5,7};
System.out.println(coinChange(coins,59));
}
public static int coinChange(int[] coins,int amount){
//硬币的数量
int n=coins.length;
//换取i元的最小硬币数量
int[] dp=new int[amount+1];
//初始条件
dp[0]=0;
//状态方程
for (int i = 1; i <dp.length; i++) {
dp[i]=Integer.MAX_VALUE;
for (int j = 0; j < n; j++) {
//边界条件
if (i-coins[j]>=0&&//钱数大于硬币数值
dp[i-coins[j]]!=Integer.MAX_VALUE&&//之前子问题没有最优解
dp[i-coins[j]]+1<dp[i])//下一个金额硬币数量大于上一个
dp[i]=dp[i-coins[j]]+1;
}
}
if (dp[amount]==Integer.MAX_VALUE)
return -1;
else
return dp[amount];
}
运行结果: