[暴力递归问题优化] 机器人到达指定位置的方法数

  算法专题导航页面


【题目描述】
    假设有排成一行的N个位置,记为1~N,开始时机器人在M位置,机器人可以往左或者往右走,如果机器人在1位置,那么下一步机器人只能走到2位置,如果机器人在N位置,那么下一步机器人只能走到N-1位置。规定机器人只能走k步,最终能来到P位置的方法有多少种。由于方案数可能比较大,所以答案需要对1e9+7取模。

输入描述:
    输出包括一行四个正整数N(2<=N<=5000)、M(1<=M<=N)、K(1<=K<=5000)、P(1<=P<=N)。

输出描述:
    输出一个整数,代表最终走到P的方法数对10^9+7模后的值。


【示例1】
输入
    5 2 3 3
输出
    3
说明
    1).2->1,1->2,2->3

    2).2->3,3->2,2->3

    3).2->3,3->4,4->3

【示例2】
输入
    1000 1 1000 1
输出
    591137401
说明
    注意答案要取模
备注:
    时间复杂度O(n*k),空间复杂度O(N)。


【代码实现 - 暴力递归】

#include<iostream>

using namespace std;

int mod = 1e9 + 7;

/*
 * Method to calculate the ways of acheiving arbitrary point
 * There are three scenarios:
 * 1. cur==1, the next step only to right
 * 2. cur==n, the next step only to left
 * 3. cur!=1 && cur!=n, the next step can be left or right
 * Note that, after each step, rest=rest-1
 */
int way(int n, int cur, int rest, int p) {
    if (2 > n || 5000 < n || 1 > cur || n < cur || 1 > rest || 5000 < rest || 1 > p || n < p) {
        return 0;
        //throw out_of_range("Input parameter(s) invalid, please check!\n");
    }
    
    if (0 == rest) {
        return cur == p ? 1 : 0;
    }
    
    if (1 == cur) {
        return way(n, cur+1, rest-1, p);
    } else if (n == cur) {
        return way(n, cur-1, rest-1, p);
    } else {
        return way(n, cur-1, rest-1, p) + way(n, cur+1, rest-1, p);
    }
}

int main() {
    int n, m, k, p;
    cin >> n >> m >> k >> p;
    int cnt = 0;
    for (int i=1; i<=n; i++) {
        cnt += way(i, m, k, p);
    }
    cout << cnt%mod <<endl;
    
    return 0;
}

    上述代码可以清晰的表示该题的解决思路,简单易懂,但是在问题规模比较大的时,容易陷入递归地狱,最终导致运行程序的机器资源耗尽。如果是在线编程,则会出现如下超时现象:
    运行超时: 您的程序未能在规定时间内运行结束,请检查是否循环有错或算法复杂度过大。Case通过率为0.00%


【代码实现 - 动态规划】

#include<iostream>
#include<vector>

using namespace std;

int mod = 1e9 + 7;

/*
 * 暴力递归优化为动态规划的条件:确认问题是否具备无后效性。
 * 当前问题中:来到某一个位置P后还剩余K步的走法仅仅与其后续走法相关,而与之前是如何来到P位置毫无关联,故其符合无后效性。
 * 优化步骤如下:
 * 1. 寻找影响递归状态的可变参数;
 * 2. 基于可变参数建立映射表,并标记出目标位置
 * 3. 根据递归中的base case计算其他未知位置的值
 * 4. 返回目标位置对应的数值
 */ 
int way2(int n, int cur, int rest, int p) {
    // 依据cur和rest建立二维映射表
    vector<vector<int>> dp(rest+1, vector<int>(n+1, 0));
    // rest==0时,只有目标位置dp[0][p]=1
    dp[0][p] = 1;
    
    // 其他位置求解参照way()方法
    for (int row=1; row<=rest; row++) {
        for (int col=1; col<=n; col++) {
            if (1 ==col) {
                dp[row][col] = dp[row-1][2]%mod;
            } else if (n == col) {
                dp[row][col] = dp[row-1][n-1]%mod;
            } else {
                dp[row][col] = (dp[row-1][col-1] + dp[row-1][col+1])%mod;
            }
        }
    }
    return dp[rest][cur]%mod;
}

int main() {
    int n, m, k, p;
    cin >> n >> m >> k >> p;
    if (2 > n || 5000 < n || 1 > m || n < m || 1 > k || 5000 < k || 1 > p || n < p) {
        return 0;
    }
    cout << way2(n, m, k, p) <<endl;
    
    return 0;
}

【代码实现 - 动态规划+空间压缩】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值