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

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

题目描述

假设有排成一行的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的方法数对 1 0 9 + 7 10^9+7 109+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*K) O(NK),空间复杂度 O ( N ) O(N) O(N)


题解:

动态规划,设 f [ i ] [ j ] f[i][j] f[i][j] 表示走了 i 步,在 j 位置的方案数,分情况讨论:

  • 若 j == 1,则 f [ i ] [ j ] = f [ i − 1 ] [ j + 1 ] f[i][j] = f[i - 1][j + 1] f[i][j]=f[i1][j+1]
  • 若 j == n,则 f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] f[i][j] = f[i - 1][j - 1] f[i][j]=f[i1][j1];
  • 否则的话, f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + f [ i − 1 ] [ j + 1 ] f[i][j] = f[i - 1][j - 1] + f[i - 1][j + 1] f[i][j]=f[i1][j1]+f[i1][j+1]

因为初始在 m 位置,将 f [ 0 ] [ m ] f[0][m] f[0][m] 设为 1,最终的结果就是 f [ k ] [ p ] f[k][p] f[k][p]

代码:
#include <cstdio>
#include <vector>

using namespace std;

const int MOD = 1e9 + 7;
const int N = 5001;

int n, m, k, p;
int f[N][N];

int main( void ) {
    scanf("%d%d%d%d", &n, &m, &k, &p);
    f[0][m] = 1;
    for ( int i = 1; i <= k; ++i ) {
        for ( int j = 1; j <= n; ++j ) {
            if ( j == 1 ) f[i][j] = f[i - 1][j + 1];
            else if ( j == n ) f[i][j] = f[i - 1][j - 1];
            else f[i][j] = (f[i - 1][j - 1] + f[i - 1][j + 1]) % MOD;
        }
    }
    return 0 * printf("%d\n", f[k][p]);
}
优化一:

我们发现,转移方程中,只跟 f[i-1] 有关,我们可以使用一个辅助数组 h 来记录 f 的上一层状态,状态转移时,直接使用上一层的状态即可。

优化一代码:
#include <cstdio>
#include <cstring>

using namespace std;

const int MOD = 1e9 + 7;
const int N = 5001;

int n, m, k, p;
int f[N];
int h[N];

int main( void ) {
    scanf("%d%d%d%d", &n, &m, &k, &p);
    int t = m - p;
    if ( t < 0 ) t = -t;
    if ( t > k ) return 0 * puts("0");
    h[m] = 1;
    for ( int i = 1; i <= k; ++i ) {
        for ( int j = 1; j <= n; ++j ) {
            if ( j == 1 ) f[j] = h[j + 1];
            else if ( j == n ) f[j] = h[j - 1];
            else f[j] = ( h[j - 1] + h[j + 1] ) % MOD;
        }
        memcpy( h, f, sizeof f );
    }
    return 0 * printf("%d\n", f[p]);
}
优化二:

观察状态转移方程,如果只用一个数组 f ,在 j 位置时,因为 f[j + 1] 保存的还是上一层的状态,可以直接使用,但是 f[j - 1] 已经被修改了,怎么办呢?我们可以设置一个临时变量提前保存 f[j - 1] 就行了。

优化二代码:
#include <cstdio>
#include <cstring>

using namespace std;

const int MOD = 1e9 + 7;
const int N = 5001;

int n, m, k, p;
int f[N];

int main( void ) {
    scanf("%d%d%d%d", &n, &m, &k, &p);
	int t = m - p;
    if ( t < 0 ) t = -t;
    if ( t > k ) return 0 * puts("0");
    f[m] = 1;
    for ( int i = 1; i <= k; ++i ) {
        int left = f[1];
        for ( int j = 1; j <= n; ++j ) {
            int tmp = f[j];
            if ( j == 1 ) f[j] = f[2];
            else if ( j == n ) f[j] = left;
            else f[j] = ( left + f[j + 1] ) % MOD;
            left = tmp;
        }
    }
    return 0 * printf("%d\n", f[p]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值