zoukankan      html  css  js  c++  java
  • 【动态规划专题】4:机器人到达指定位置的方法数

    《程序员代码面试指南--IT名企算法与数据结构题目最优解》 左程云 著

    机器人到达指定位置方法数

    【题目】
    假设有排成一行的N个位置,记为1~N,N一定大于或等于2。开始时机器人在其中的M位置上(M一定是1~N中的一个),
    机器人可以往左走或者往右走,如果机器人来到1位置,那么下一步只能往右来到2位置;
    如果机器人来到N位置,那么下一步只能往左来到N-1位置。
    规定,机器人必须走K步,最终能来到P位置(P也一定是1~N中的一个)的方法有多少种。
    给定4个参数N、M、K、P,返回方法数。

    【要求】
    时间复杂度为O(N*K)

    #include <algorithm>
    #include <iostream>
    #include <stack>
    #include <vector>
    #include <exception>
    using namespace std;

    方法1:暴力递归求解(暴力递归建模比较困难(至少对于我来说是如此))

    //////////////////////////////////////////暴力递归求解
    //N:位置为1~N,固定参数
    //cur:当前在cur位置,可变参数
    //rest:还剩res步没有走,可变参数
    //P:最终目标位置P,固定参数
    //只能在1~N这些位置上移动,当前在cur位置,走完rest步之后,停在P位置的方法数作为返回值返回
    //
    int walk(int N, int cur, int rest, int P)
    {
        //如果没有剩余步数了,当前的cur位置就是最后的位置
        //如果最后的位置停在P上,那么之前做的移动是有效的
        //如果最后的位置没有停在P上,那么之前做的移动是无效的
        if (rest == 0)
        {
            return cur == P ? 1 : 0;
        }
    
        //如果还有rest步要走,而当前的cur位置在1位置上,那么当前这步只能从1走向2
        //后续的过程就是原来到2位置上,还剩rest-1步要走
        if (cur == 1)
        {
            return walk(N, 2, rest - 1, P);
        }
    
        //如果还有rest步要走,而当前的cur位置在N位置上,那么当前这步只能从N走向N-1
        //后续的过程就是来到N-1位置上,还剩rest-1步要走
        if (cur == N)
        {
            return walk(N, N - 1, rest - 1, P);
        }
    
        //如果还有rest步要走,而当前的cur位置在中间位置上,那么可以向左走,也可以向右走
        //
        return walk(N, cur - 1, rest - 1, P) + walk(N, cur + 1, rest - 1, P);
    }
    
    int ways1(int N, int M, int K, int P)
    {
        //参数无效直接返回
        if (N < 2 || K < 1 || M<1 || M>N || P<1 || P>N)
        {
            return 0;
        }
    
        //总共N个位置,从M点出发,还剩K步,返回最终能达到P的方法数
        return walk(N, M, K, P);
    }

    方法2:通过暴力递归,分析模型,然后用循环的方法求解

    /////////////////////////////////////递归思路分析,循环方法解决
    int ways2(int N, int M, int K, int P)
    {
        //参数无效直接返回0
        if (N < 2 || K < 1 || M<1 || M>N || P<1 || P>N)
        {
            return 0;
        }
        int iResult = 0;
        int** dp = new int*[K + 1];
        for (int i = 0; i < K + 1; i++)
        {
            dp[i] = new int[N + 1];
        }
    
        //给第1行赋值,如果走0步,只有在p点才是有效的,其他位置无效,为0
        for (int i = 0; i < N + 1; i++)
        {
            if (i != P)
            {
                dp[0][i] = 0;
            }
            else
            {
                dp[0][i] = 1;
            }
        }
        for (int j = 0; j < K + 1; j++)
        {
            dp[j][0] = 0;
        }
    
        for (int i = 1; i < K + 1; i++)
        {
            for (int j = 1; j < N + 1; j++)
            {
                if (j == 1)
                {
                    dp[i][j] = dp[i-1][2];
                }
                else if (j == N)
                {
                    dp[i][j] = dp[i - 1][N - 1];
                }
                else
                {
                    dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1];
                }
            }
        }
    
    
    
        /////打印这个辅助数组
        printf("dp[][]------------way2----------------start
    ");
        for (int i = 0; i < K + 1; i++)
        {
            for (int j = 0; j < N + 1; j++)
            {
                printf("%5d,", dp[i][j]);
            }
            printf("
    ");
        }
        printf("dp[][]-------------way2---------------end
    ");
        
    
        iResult = dp[K][M];
    
        for (int i = 0; i < K + 1; i++)
        {
            delete[] dp[i];
        }
        delete[] dp;
    
        return iResult;
    }

    方法3:在方法2的基础上,对申请的内存空间做优化。只申请一个一维的数组空间。

    ///压缩到一维数组
    int ways3(int N, int M, int K, int P)
    {
        //参数无效直接返回0
        if (N < 2 || K < 1 || M<1 || M>N || P<1 || P>N)
        {
            return 0;
        }
        int iResult = 0;
        int* dp = new int[N + 1];
    
        //给第1行赋值,如果走0步,只有在p点才是有效的,其他位置无效,为0
        for (int i = 0; i < N + 1; i++)
        {
            if (i != P)
            {
                dp[i] = 0;
            }
            else
            {
                dp[i] = 1;
            }
        }
    
        for (int i = 1; i < K + 1; i++)
        {
            int LeftUp = dp[1];
            for (int j = 1; j < N + 1; j++)
            {
                int tempLeft = dp[j];
                if (j == 1)
                {
                    dp[j] = dp[j+1];
                }
                else if (j == N)
                {
                    dp[j] = LeftUp;
                }
                else
                {
                    dp[j] = LeftUp + dp[j + 1];
                }
    
                LeftUp = tempLeft; ///保存旧的的左值
            }
        }
    
    
    
    
        iResult = dp[M];
        delete[] dp;
    
        return iResult;
    }

    测试用例很重要。要用若干个测试用例去覆盖可能的各种情况。增强程序健壮性。

    //==================测试用例====================
    void test1()
    {
        //5,2,3,3,
        //7,4,9,5
        int N = 0;
        int M = 0;
        int K = 0;
        int P = 0;
        cout << "Test1---------------------------------" << endl;
        N = 7;    M = 4;    K = 9;    P = 5;
        //cout << "way1:" << ways1(N, M, K, P) << endl;;
        cout << "way2:" << ways2(N, M, K, P) << endl;;
        cout << "way3:" << ways3(N, M, K, P) << endl << endl;;;
    }
    
    void test2()
    {
        //5,2,3,3,
        //7,4,9,5
        int N = 0;
        int M = 0;
        int K = 0;
        int P = 0;
        cout << "Test2---------------------------------" << endl;
        N = 7;    M = 2;    K = 13;    P = 5;
        //cout << "way1:" << ways1(N, M, K, P) << endl;;
        cout << "way2:" << ways2(N, M, K, P) << endl;;
        cout << "way3:" << ways3(N, M, K, P) << endl << endl;;
    }
    
    
    void test3()
    {
        int N = 0;
        int M = 0;
        int K = 0;
        int P = 0;
        cout << "Test3---------------------------------" << endl;
        N = 5;    M = 2;    K = 3;    P = 3;
        //cout << "way1:" << ways1(N, M, K, P) << endl;;
        cout << "way2:" << ways2(N, M, K, P) << endl;;
        cout << "way3:" << ways3(N, M, K, P) << endl << endl;;
    }
    
    int main()
    {
        test1();
        test2();
        test3();
    
        cout << endl;
        system("pause");
        return 0;
    }

    这里展示部分的运行结果:

    Test3---------------------------------
    dp[][]------------way2----------------start
        0,    0,    0,    1,    0,    0,
        0,    0,    1,    0,    1,    0,
        0,    1,    0,    2,    0,    1,
        0,    0,    3,    0,    3,    0,
    dp[][]-------------way2---------------end
    way2:3
    way3:3
  • 相关阅读:
    Notes about "Exploring Expect"
    Reuse Sonar Checkstyle Violation Report for Custom Data Analysis
    Eclipse带参数调试的方法
    MIT Scheme Development on Ubuntu
    Manage Historical Snapshots in Sonarqube
    U盘自动弹出脚本
    hg的常用配置
    Java程序员的推荐阅读书籍
    使用shared memory 计算矩阵乘法 (其实并没有加速多少)
    CUDA 笔记
  • 原文地址:https://www.cnblogs.com/music-liang/p/12073620.html
Copyright © 2011-2022 走看看