组合问题与动态规划的联系之应用

一,问题描述

假设有个机器人坐在 X×Y 网格的最左上角,每次只能向下或者向左移动。最左上角的坐标标记为(0,0),最右下角的坐标为(X,Y)

请问:机器人从(0,0)走到(X,Y)共有多少种走法?其实这个问题与 这篇文章 中提到的问题非常相似。

二,问题分析

这个问题一共有三种方式来求解。第一种是使用公式;第二种是使用递归;第三种是使用动态规划

使用递归和动态规划,其实本质上是一致的。都是使用组合原理进行问题分析。

机器人从(0,0)走到(X,Y)一共需要走 X+Y步。其中必须有X步是向下走的(因为最终的横坐标是X)。问题转化为:从X+Y步走法中,选出X步是向下走,一共有多少种选法?这是一个组合问题了。答案是C(X+Y,X)

对于(X,Y),一共有两种情况:从(X-1,Y)向下走一步到达(X,Y);从(X,Y-1)向右走一步到达(X,Y)

设steps(X,Y)表示从(0,0)走到(X,Y)一共用的方式数,那么 steps(X,Y)=steps(X-1,Y)+steps(X,Y-1)

初始条件:steps(0,0)=1;steps(x,0)=steps(0,y)=1

因此,就可以代表上面的公式使用递归或者DP求解了。

三,代码实现

public class Steps {

    public static int steps(int x, int y)
    {
        if(x < 0 || y < 0)
            throw new IllegalArgumentException();
        return steps_recur(x, y);
    }

    //使用递归来求解
    private static int steps_recur(int x, int y)
    {
        assert x >=0 || y >= 0;
        if(x == 0 || y == 0)
            return 1;
        return steps_recur(x - 1, y) + steps_recur(x, y - 1);
    }

    //dp resolve
    public static int steps_dp(int x, int y)
    {
        if(x < 0 || y < 0)
            throw new IllegalArgumentException();

        int[][] dp = new int[x + 1][y + 1];

        //dp的初始条件
        for(int i = 0; i <= x; i++)
            dp[i][0] = 1;//y==0,说明只能向右走
        for(int i = 0; i <= y; i++)
            dp[0][i] = 1;//x==0,说明只能往下走

        //状态方程的实现,for循环从1开始,充分体现了自底向上的思想
        for(int i = 1; i <= x; i++)
        {
            for(int j = 1; j <= y; j++)
            {
                dp[i][j] = dp[i-1][j] + dp[i][j - 1];
            }
        }
        return dp[x][y];
    }

    //使用公式来求解
    public static int steps_factorial(int x, int y){
        if(x < 0 || y < 0)
            throw new IllegalArgumentException();
        return factorial(x + y) / (factorial(x) * factorial(y));
    }

    //求n!
    public static int factorial(int n){
        if(n < 0)
            throw new IllegalArgumentException();
        int res = 1;
        for(int i = 1; i <= n; i++)
            res *= i;
        return res;//0!=1
    }

    //test
    public static void main(String[] args) {
        int x = 1;
        int y = 5;
        System.out.println("dp solve:" + steps_dp(x, y));
        System.out.println("formula solve:" + steps_factorial(x, y));
        System.out.println("recursive solve:" + steps(x, y));
    }
}

四,参考资料

动态规划之Fib数列类问题应用

排列与组合的一些定理(二)

排列与组合的一些定理

时间: 2024-10-09 09:29:06

组合问题与动态规划的联系之应用的相关文章

【noi 2.6_9288】&amp;【hdu 1133】Buy the Ticket(DP / 排列组合 Catalan+高精度)

题意:有m个人有一张50元的纸币,n个人有一张100元的纸币.他们要在一个原始存金为0元的售票处买一张50元的票,问一共有几种方案数. 解法:(学习了他人的推导后~) 1.Catalan数的应用7的变形.(推荐阅读:http://www.cnblogs.com/chenhuan001/p/5157133.html).P.S.不知我之前自己推出的公式“C(n,m)*C(2*m,m)/(m+1)*P(n,n)*P(m,m)”是否是正确的. (1)在不考虑m人和n人本身组内的排列时,总方案数为C(m+

动态规划之Fib数列类问题应用

一,问题描述 有个小孩上楼梯,共有N阶楼梯,小孩一次可以上1阶,2阶或者3阶.走到N阶楼梯,一共有多少种走法? 二,问题分析 DP之自顶向下分析方式: 爬到第N阶楼梯,一共只有三种情况(全划分,加法原理),从第N-1阶爬1阶到第N阶:从第N-2阶爬2阶到第N阶:从第N-3爬3阶到第N阶. 故:way(N)=way(N-1)+way(N-2)+way(N-3) 这与求Fib数列非常相似,当然,其他类似的问题也可以这样求解. 初始条件: way(1)=1 way(2)=2 way(3)=4 三,代码

CH5201 数组组合【01背包】

5201 数字组合 0x50「动态规划」例题 描述 在N个数中找出其和为M的若干个数.先读入正整数N(1<N<100)和M(1<M<10000), 再读入N个正数(可以有相同的数字,每个数字均在1000以内), 在这N个数中找出若干个数, 使它们的和是M, 把满足条件的数字组合都找出来以统计组合的个数,输出组合的个数(不考虑组合是否相同). 输入格式 第一行是两个数字,表示N和M.第二行起是N个数. 输出格式 就一个数字,表示和为M的组合的个数. 样例输入 4 4 1 1 2 2

LeetCode39Combination Sum

题目如下: Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target. The same repeated number may be chosen from candidates unlim

动态规划(0-1背包)--- 组合总和

组合总和 377. Combination Sum IV (Medium) nums = [1, 2, 3] target = 4 The possible combination ways are: (1, 1, 1, 1) (1, 1, 2) (1, 2, 1) (1, 3) (2, 1, 1) (2, 2) (3, 1) Note that different sequences are counted as different combinations. Therefore the ou

UVALive-7304 - Queue of Soldiers 【动态规划】【组合函数】【好题】

UVALive- 7304 - Queue of Soldiers 题目链接:7304 题目大意:士兵过山洞,必须以类似7 6 5 4 3 2 1顺序过.在第i个人之后,比i高的人都会被杀死,问如果要杀死k个人,有几种排队方法. 题目思路:先将士兵的身高离散化.假设N表示不同身高的数目.cnt[i] 表示i这个身高的人有多少个.(i的范围为1~N)sum[i]表示小于等于该身高段的士兵数目 然后开始dp,dp[i][j]表示已经到第i个士兵,已经死了j个人的方法数. 第三维遍历,q表示,第i+1

9.9递归和动态规划(六)——打印n对括号的所有有效组合(即左右括号正确配对)

/** * 功能:打印n对括号的所有有效组合(即左右括号正确配对). */ 两种方法: 方法一: /** * 思路:在括号的最前面或者原有的每对括号中面插入一对括号. 至于其它任何位置.比方字符串的末尾,都会跟之前的情况反复. * 注意:将字符串放进结果列表之前.必须检查列表有无反复. * @param remaining * @return */ public static HashSet<String> generateParens(int remaining){ HashSet<S

9.9递归和动态规划(六)——打印n对括号的全部有效组合(即左右括号正确配对)

/** * 功能:打印n对括号的全部有效组合(即左右括号正确配对). */ 两种方法: 方法一: /** * 思路:在括号的最前面或者原有的每对括号里面插入一对括号.至于其他任意位置,比如字符串的末尾,都会跟之前的情况重复. * 注意:将字符串放进结果列表之前,必须检查列表有无重复. * @param remaining * @return */ public static HashSet<String> generateParens(int remaining){ HashSet<St

9.9递归和动态规划(五)——确定某字符串的所有排列组合

/** * 功能:确定某字符串的所有排列组合. */ 注意:不考虑重复字符.若考虑重复字符,只需在加入permulations时去掉重复的字符串即可. /** * 思路:元素由少到多,将新的元素塞进所有字符串中间的任意可能位置. * @param str * @return */ public static ArrayList<String> getPerms(String str){ if(str==null) return null; ArrayList<String> per