圆环回原点问题
约 770 字大约 3 分钟
2025-03-01
圆环上有10个点,编号为0~9。从0点出发,每次可以逆时针和顺时针走一步,问走n步回到0点共有多少种走法。
输入: 2
输出: 2
解释:有2种方案。分别是0->1->0和0->9->0
动态规划
dp[i][j]
表示从0出发,走 i 步到 j 的方案数。
走n步到0的方案数 = 走n-1步到1的方案数 + 走n-1步到9的方案数。
状态转移公式要取余,因为j-1或j+1可能会超过圆环0~9的范围
public class Solution {
public int backToOrigin(int n) {
// 点的个数为10
int length = 10;
// 创建二维数组dp。dp[i][j] 表示从0出发,走 i 步到 j 的方案数。
int[][] dp = new int[n + 1][length];
// dp[0][0] 表示从0出发,走0步到0的方案数为1,dp[0][j]为默认值0
dp[0][0] = 1;
// 遍历步数,从1到n
for (int i = 1; i <= n; i++) {
// 遍历点的位置,从0到length-1
for (int j = 0; j < length; j++) {
// j-1有可能为负,所以要加length把它变正,再取余。j+1加不加length都一样。
dp[i][j] = dp[i - 1][(j - 1 + length) % length] + dp[i - 1][(j + 1) % length];
}
}
// 返回dp[n][0],即走n步回到0的方案数
return dp[n][0];
}
}
滚动更新
public class Solution {
public int backToOrigin(int n) {
// 点的个数为10
int length = 10;
// 使用一维数组 dp 存储当前步数的方案数
int[] dp = new int[length];
dp[0] = 1; // 第一行的 dp[0] 表示从0出发,走0步到0的方案数为1,第n行的dp[0]表示从0出发,走n步到0的方案数。
// 临时数组 temp 用于存储更新后的方案数
int[] temp = new int[length];
// 遍历步数,从1到n。temp指向本轮数组,dp是上一轮数组,计算完dp前进一步,即前进到temp。
for (int i = 1; i <= n; i++) {
// 每次步数更新时,将 temp 数组初始化为 0
Arrays.fill(temp, 0);
// 遍历点的位置,从0到length-1
for (int j = 0; j < length; j++) {
// temp[j] 表示从0出发,走i步到j的方案数
// temp[j] = dp[(j-1+length)%length] + dp[(j+1)%length]
// 为了保证dp[k]都是上一轮的值,我们这里不能直接更新dp数组,要先用临时数组保存。
temp[j] = dp[(j - 1 + length) % length] + dp[(j + 1) % length];
}
// 将 temp 数组赋值给 dp 数组,以进行下一次步数的更新
System.arraycopy(temp, 0, dp, 0, length);
}
// 返回 dp[0],即走 n 步回到 0 的方案数
return dp[0];
}
}
System.arraycopy
是标准且高效的选择,特别是在需要频繁复制大量数据的场景中。它是 Java 中处理数组复制的惯用方法之一,尤其是在涉及到性能敏感的应用时。
Arrays.copyOf
更适合需要返回一个新数组的情况,而 System.arraycopy
则适用于对现有数组的部分或全部内容进行复制。