【動態規划專題】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\r\n");
    for (int i = 0; i < K + 1; i++)
    {
        for (int j = 0; j < N + 1; j++)
        {
            printf("%5d,", dp[i][j]);
        }
        printf("\r\n");
    }
    printf("dp[][]-------------way2---------------end\r\n");
    

    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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM