

机器人到达指定位置的方法数问题 - Grey Zeng
source link: https://www.cnblogs.com/greyzeng/p/16837512.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

机器人到达指定位置的方法数问题
作者:Grey
原文地址:
题目描述#
链接:https://www.nowcoder.com/questionTerminal/54679e44604f44d48d1bcadb1fe6eb61
来源:牛客网
假设有排成一行的N个位置,记为1~N,开始时机器人在M位置,机器人可以往左或者往右走,如果机器人在1位置,那么下一步机器人只能走到2位置,如果机器人在N位置,那么下一步机器人只能走到N-1位置。规定机器人只能走k步,最终能来到P位置的方法有多少种。由于方案数可能比较大,所以答案需要对1e9+7取模。
暴力解#
定义递归函数
long ways(int len, int start, int step, int end)
递归含义表示:机器人从坐标 start 开始,只能走 step 步,到达 end 的方法数是多少。
接下来是 base case,
if (step == 0) {
if (start == end) {
return 1L;
}
return 0L;
}
如果 step 只剩下 0 步,说明没有步数可以走了,此时,如果 start == end
,表示正好就在目的地,返回一种方法数;
否则,返回 0 种方法数。
接下来是普遍情况,如果 start == 0
,只能向右边走,即: ways(len, start + 1, step - 1, end)
;
如果 start == len - 1
,只能向左边走,即:ways(len, start - 1, step - 1, end)
;
不在两端位置,则既可以向左边走,也可以向右边走,即:(ways(len, start - 1, step - 1, end) + ways(len, start + 1, step - 1, end))
。
暴力解法完整代码如下
public static long ways(int len, int start, int step, int end) {
if (step == 0) {
if (start == end) {
return 1L;
}
return 0L;
}
// step不止一步
if (start == 0) {
return ways(len, start + 1, step - 1, end);
} else if (start == len - 1) {
return ways(len, start - 1, step - 1, end);
} else {
return (ways(len, start - 1, step - 1, end) + ways(len, start + 1, step - 1, end));
}
}
动态规划-缓存法(可 AC)#
上述暴力递归过程,可变参数有两个 start,step;可以设置一个二维数组 dp,用于缓存递归中间过程的解,
long[][] dp = new long[len][step + 1];
dp[i][j]
表示ways(len,i,j,end)
的值,dp
数组的值均初始化为 -1。
上述暴力递归过程增加这个 dp 参数,如果dp[i][j] != -1
,则说明已经算过这个递归过程,直接返回dp[i][j]
的值即可。
完整代码如下
public static long ways(int len, int start, int step, int end) {
long[][] dp = new long[len][step + 1];
for (int i = 0; i < len; i++) {
for (int j = 0; j <= step; j++) {
dp[i][j] = -1L;
}
}
dp(len, start, step, end, dp);
return dp[start][step];
}
private static long dp(int len, int start, int step, int end, long[][] dp) {
if (dp[start][step] != -1L) {
return dp[start][step] % MOD;
}
if (step == 0) {
dp[start][step] = start == end ? 1L : 0L;
return dp[start][step];
}
long ans;
// step不止一步
if (start == 0) {
ans = dp(len, start + 1, step - 1, end, dp);
} else if (start == len - 1) {
ans = dp(len, start - 1, step - 1, end, dp);
} else {
ans = (dp(len, start - 1, step - 1, end, dp) + dp(len, start + 1, step - 1, end, dp));
}
dp[start][step] = ans;
return ans;
}
动态规划解-严格位置依赖(可 AC)#
回到暴力递归解,伪代码如下
public static long ways(int len, int start, int step, int end) {
……
if (start == 0) {
return ways(len, start + 1, step - 1, end);
} else if (start == len - 1) {
return ways(len, start - 1, step - 1, end);
} else {
return (ways(len, start - 1, step - 1, end) + ways(len, start + 1, step - 1, end));
}
}
根据缓存法得知,该递归过程使用一个二维数组 dp 即可存下所有结果,其中
dp[i][j]
表示ways(len,i,j,end)
的值,
通过观察上述暴力递归过程,dp[i][j]
依赖的位置是dp[i+1][j-1]
,dp[i-1][j-1]
,
所以,依据上述递归过程,可以改成严格位置的动态规划版本,完整代码如下
public static long ways(int len, int start, int step, int end) {
long[][] dp = new long[len][step + 1];
// 填好第0列
dp[end][0] = 1L;
for (int j = 1; j < step + 1; j++) {
for (int i = 0; i < len; i++) {
if (i == 0) {
dp[i][j] = dp[1][j - 1];
} else if (i == len - 1) {
dp[i][j] = dp[len - 2][j - 1];
} else {
dp[i][j] = dp[i - 1][j - 1] % MOD + dp[i + 1][j - 1] % MOD;
}
}
}
return dp[start][step];
}
动态规划解-空间压缩版(可 AC)#
通过上述严格位置的动态规划版本可以得知,dp[i][j]
位置,依赖的位置是dp[i+1][j-1]
,dp[i-1][j-1]
,
示例图如下
而且,通过上述动态规划解,可以得知第 0 列中,除了dp[end][0] = 1L
,其余都是 0,所以 dp 的第0列可以直接填充
所以,只需要用一个一维数组表示第 0 列,然后根据依赖关系,通过第 0 列推出第一列的值,一维数组此时表示第一列的值,依次这样递推下去,一直到最后一列,得解,这种方法就可以将二维数组压缩成一维数组,节省了空间复杂度。
完整代码如下
public static long ways(int len, int start, int step, int end) {
long[] dp = new long[len];
dp[end] = 1L;
long tmp = 0;
for (int j = 1; j < step + 1; j++) {
for (int i = 0; i < len; i++) {
long ways = dp[i];
if (i == 0) {
dp[i] = dp[1] % MOD;
} else if (i == len - 1) {
dp[i] = tmp % MOD;
} else {
dp[i] = tmp % MOD + dp[i + 1] % MOD;
}
tmp = ways;
}
}
return dp[start];
}
更多#
Recommend
-
7
作者:Grey 原文地址: 使用加强堆结构解决topK问题 题目描述
-
3
生成二维码,并且保存,指定位置的view成图片,并且保存到本地相册 保存的图片效果是:
-
12
作者:Grey 原文地址:单词搜索问题 题目链接
-
4
作者:Grey 原文链接:正则表达式匹配问题 问题链接
-
6
作者: Grey 原文地址:使用并查集解决的相关问题 关于并查集的说明,见如下博客:
-
9
作者:Grey 原文地址: 完美洗牌问题 问题描述
-
9
Linux 下指定端口开放访问权限 作者:Grey 原文地址: ...
-
7
设计模式学习(十四):模板方法 作者:Grey 原文地址: 博...
-
9
Feign 实现 GET 方法传递 POJO 作者:Grey 原文地址: ...
-
4
Schtasks CLI 工具指定開始位置及引數-黑暗執行緒 題目很簡單,我想使用指令工具建排程,建立時一併指定啟動引數(Argument)及開始位置:
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK