【动态规划专题】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
原文地址:https://www.cnblogs.com/music-liang/p/12073620.html