动态规划-九章算法01

problem solving steps

step1:

确定状态:

  • 最后一步
  • 子问题
step2:

转移方程

step3:

初始条件和边界情况

step4:

确定计算顺序

example1

https://www.lintcode.com/problem/coin-change/
在这里插入图片描述

step1:(确定状态)

不妨我们先设总金额共27元,面值为2,5,7

最后一步:
  • 虽然我们不知道最优策略是什么,但是最优策略一定是a1,a2,···,ak共k枚硬币加起来面值等于27,所以一定有一枚最后的硬币:ak,那除掉这最后的一枚硬币,前面的硬币面值加起来应该为27-ak
    在这里插入图片描述
子问题:
  • 所以我们就要求:用最少的硬币可以拼出27-ak
  • 原问题是最少用多少枚硬币可以拼出27
  • 我们将原问题转化为一个子问题,而且规模更小:27-ak
  • 为了简化定义,我们设状态f(X)=最少用多少枚硬币拼出X
  • 等等,我们还不清楚最后那枚硬币ak是多少
  • 最后那枚硬币只能是2,5或者7
  • 如果ak为2,f(27)应该是f(27-2)+1(加上最后这枚硬币2)
  • 如果ak为5,f(27)应该是f(27-5)+1(加上最后这枚硬币5)
  • 如果ak为7,f(27)应该是f(27-7)+1(加上最后这枚硬币7)
  • 除此之外,没有其他可能了
  • 由于要求最少的硬币数目,故:
  • f(27) = min{f(27-2)+1 , f(27-5)+1,f(27-7)+1}
step2(转移方程):
  • 设状态f[x]=最少用多少枚硬币拼出X
  • 对于任意的X,在这里插入图片描述
step3(初始条件和边界情况):
  • f[x] = min{f[x-2]+1 , f[x-5]+1,f[x-7]+1}
  • 两个问题:x-2,x-5,x-7小于0怎么办?什么时候停下来?
  • 如果不能拼出Y,就定义Y为正无穷:
  • 例如f(-1)=f(-2)=···=正无穷
  • 初始条件:f[0]=0
  • 对于初始条件f[0],若不手动定义,则用转移方程算出来为正无穷,然而f[0]应为0,则需手动定义
step4(计算顺序):
  • f[x] = min{f[x-2]+1 , f[x-5]+1,f[x-7]+1}
  • 当我们计算f[x]时,f[x-2] , f[x-5],f[x-7]需已知
  • 初始条件:f[0]=0
  • 然后计算f[1]、f[2]······、f[27]
class Solution {
public:
    /**
     * @param coins: a list of integer
     * @param amount: a total amount of money amount
     * @return: the fewest number of coins that you need to make up
     */
    int coinChange(vector<int> &coins, int amount) {
        // write your code here
        vector<int>f(amount+1);
        f[0] = 0;
        for(int i=1;i <= amount;++i){
            f[i] = 100000;//maxvalue
            for(int j=0;j<coins.size();++j){
                if(i-coins[j]>=0 && coins[j]!=0){//coins[j]!=0:because the array that the question given include number-zero
                    f[i] = min(f[i-coins[j]]+1,f[i]);
                }
            }
        }
        if(f[amount]==100000){
            return -1;
        }
        return f[amount];
        
    }
};

example2

https://www.lintcode.com/problem/114/

在这里插入图片描述

step1:确定状态
最后一步
  • 无论机器人怎么走到右下角,总有最后一步:向右或者向下
  • 右下角坐标设为(m-1,n-1)
  • 那么前一步机器人一定在(m-2,n-1)或者(m-1,n-2)
子问题
  • 若机器人有M种方式走到(m-2,n-1),有N种方式走到(m-1,n-2),则有M+N种方式走到(m-1,n-1)
  • 设f[i][j]代表机器人有多少种方式走到(i,j)
step2:转移方程

f[i][j] = f[i-1][j] + f[i][j-1]

step3:初始条件和边界情况
初始条件:
  • f[0][0] = 1,因为机器人只有一种方式走到左上角
边界情况:
  • i=0 或 j=0 时,则f[i][j] = 1
step4:计算顺序
  • 先行,后列
  • f[0][0],f[0][1]…,f[0][j-1]
  • f[i-1][0],f[i-1][1],…,f[i-1,j-1]
class Solution {
public:
    /**
     * @param m: positive integer (1 <= m <= 100)
     * @param n: positive integer (1 <= n <= 100)
     * @return: An integer
     */
    int uniquePaths(int m, int n) {
        // write your code here
        vector< vector<int> >f(m,vector<int>(n));//int f[m][n]效率高一些
        for(int i = 0;i < m; ++i){
            for(int j=0;j<n;++j){
                if(i==0 || j==0){
                    f[i][j] = 1;
                }else{
                    f[i][j] = f[i-1][j] + f[i][j-1];
                }
            }
        }
        return f[m-1][n-1];
    }
};

example 3

https://www.lintcode.com/problem/jump-game/
在这里插入图片描述

step1:确定状态
最后一步
  • 设最后一步从石头i跳过来
  • 需要满足两个条件:
  • 1.能跳到石头i
  • 2.最后一步不能超过跳跃的最大距离:n-1-i<=ai
子问题

设f[i]代表能不能跳到石头i

step2:转移方程

在这里插入图片描述

step3:初始条件和边界情况
初始条件:

f[0] = true

边界情况:

step4:计算顺序

从小到大

class Solution {
public:
    /**
     * @param A: A list of integers
     * @return: A boolean
     */
    bool canJump(vector<int> &A) {
        // write your code here
        vector<bool>f(A.size());
        f[0] = true;
        for(int i=1;i<A.size();++i){
            int j;
            for(j=0;j<i;++j){
                if(f[j]==true && i-j<=A[j]){
                    f[i] = true;
                    break;
                }
            }
            if(j==i){
                f[i] = false;
            }
        }
        return f[A.size()-1];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值