机器人到达指定位置方法数
题目描述
假设有排成一行的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(N∗K),空间复杂度 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[i−1][j+1];
- 若 j == n,则 f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] f[i][j] = f[i - 1][j - 1] f[i][j]=f[i−1][j−1];
- 否则的话, 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[i−1][j−1]+f[i−1][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]);
}