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

简介:

一,问题描述

 假设有个机器人坐在 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)

 

还有另一种理解方式:

由于机器人不能往回走,只能向下或者向左走。因此,将向下走记为 Down,向左走记为Left。问题就转化为{X·Down,  Y·Left}的一个全排列问题。

即:集合{X·Down,  Y·Left}有两个元素,Down和Left。Down一共有X个,Left一共有Y个。

从(0,0)走到(X,Y)就相当于对集合所有的元素进行全排列。由于这是一个“重集合”,故全排列数为 (X+Y)!/X!·Y!

其中,(X+Y)!/X!·Y!  等于 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数列类问题应用

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

排列与组合的一些定理



本文转自hapjin博客园博客,原文链接:http://www.cnblogs.com/hapjin/p/5699522.html,如需转载请自行联系原作者

相关文章
|
4月前
|
机器学习/深度学习 消息中间件 Kubernetes
动态规划-线性DP问题总结(一)
动态规划-线性DP问题总结(一)
|
4月前
|
消息中间件 Kubernetes NoSQL
动态规划-线性DP问题总结(二)
动态规划-线性DP问题总结(二)
|
7月前
|
存储 人工智能 自然语言处理
动态规划算法总结
动态规划算法总结
|
7月前
|
存储 人工智能 分布式计算
动态规划从理论到实践-深入理解贪心/分治/回溯/动态规划的算法思想
动态规划从理论到实践-深入理解贪心/分治/回溯/动态规划的算法思想
|
10月前
|
存储 算法 搜索推荐
动态规划算法
动态规划算法是一种常用的优化问题求解方法,主要用于解决具有重叠子问题和最优子结构性质的问题。动态规划算法的基本思想是将原问题拆分成若干个子问题,通过求解子问题的最优解来求解原问题的最优解。动态规划算法通常包含以下三个步骤:
74 2
|
存储 算法
【回溯算法篇】组合问题(上)
【回溯算法篇】组合问题(上)
【回溯算法篇】组合问题(上)
【回溯算法篇】组合问题(下)
【回溯算法篇】组合问题(下)
【回溯算法篇】组合问题(下)
【动态规划法】0-1背包问题
【动态规划法】0-1背包问题
120 0
【动态规划法】0-1背包问题
|
人工智能 算法
【动态规划】矩阵连乘
完全加括号的矩阵连乘积可递归地定义为: • 单个矩阵是完全加括号的 • 矩阵连乘积A是完全加括号的,则A可表示为2个完全加括号的矩阵连乘积B和C 的乘积并加括号,即A=(BC) 设有四个矩阵A, B, C, D ,它们的维数分别是: A = 50*10 B = 10*40 C = 40*30 D = 30*5 总共有五种完全加括号的方式:
277 0