问题陈述:
您位于位置(x1,x2,...,xN)的N维网格中。网格的尺寸为(D1,D2,...... DN)。在一个步骤中,您可以在N个维度中的任何一个中前进或后退一步。 (因此总有2×N可能的不同动作)。你有多少种方法可以采取M步,这样你就不会在任何时候离开网格?如果在任何点xi,你离开网格,xi≤0或xi> Di。
输入格式
第一行包含测试用例的数量T.T测试用例如下。对于每个测试用例,第一行包含N和M,第二行包含x1,x2,...,xN,第3行包含D1,D2,...,DN。
输出格式
输出T行,一行对应于每个测试用例。由于答案可能非常大,因此输出模数为1000000007。
约束
1≤T≤10
1≤N≤10
1≤M≤300
1≤Di≤100
1≤xi≤Di
示例输入
1
2 3
1 1
2 3
示例输出
12
如果这是 1D ,解决方案可以是这样的:solve(i + 1)+ solve(i-1);
在 2D :solve(i + 1,j)+ solve(i-1,j)+ solve(i,j + 1)+ solve(i,j-1);我如何为N Dimensions编程?它们是如上所述制作递归语句的一些常规步骤,可以帮助制作递归语句
我看到的大多数解决方案都是自下而上或自上而下的方式我无法理解它们?是他们理解他们的任何方式,因为我一直使用递归+ memoization练习dp我觉得很难理解它们
怎么做备忘录?要使用哪种数据结构?,在哪里使用modulo 1e 7(仅在最终答案中)??
更新 感谢BARNEY for SOLUTION ,但在大多数情况下获得 TLE 如何更快地完成 代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
public class Grid_Walking {
private static String[] n;
private static int moves;
private static HashMap<String, Integer> hm;
private static BufferedReader br;
private static String[] s;
private static int dimen;
private static int[] present;
private static int[] dimlen;
public static void main(String args[]) throws IOException {
br = new BufferedReader(new InputStreamReader(System.in));
int t = Integer.parseInt(br.readLine());
while (t > 0) {
n = br.readLine().split(" ");
dimen = Integer.parseInt(n[0]);
present = new int[dimen];
dimlen = new int[dimen];
moves = Integer.parseInt(n[1]);
s = br.readLine().split(" ");
for (int i = 0; i < dimen; i++) {
present[i] = Integer.parseInt(s[i]) - 1;
}
s = br.readLine().split(" ");
for (int i = 0; i < dimen; i++) {
dimlen[i] = Integer.parseInt(s[i]);
}
hm = new HashMap<String, Integer>();
System.out.println(solve(present, moves) % 1000000007);
t--;
}
}
private static int solve(int[] x, int moves) {
// TODO Auto-generated method stub
int result = 0;
String s = Arrays.toString(x) + moves;
if (hm.containsKey(s)) {
return hm.get(s);
}
if (moves == 0) {
return 1;
}
for (int i = 0; i < dimen; i++) {
// System.out.println("fjfvnfjbv");
if (x[i] > 0) {
x[i] = x[i] - 1;
result = result + solve(x, moves - 1);
x[i] = x[i] + 1;
}
if (x[i] < dimlen[i] - 1) {
x[i] = x[i] + 1;
result = result + solve(x, moves - 1);
x[i] = x[i] - 1;
}
}
hm.put(s, result % 1000000007);
return result % 1000000007;
}
}
答案 0 :(得分:7)
使用两级DP方法可以通过以下方式有效地解决此问题:
首先,分别解决每个维度的问题。这意味着对于每个维度,计算仅在该维度中生成0,1,...,M步骤的方式的数量(从给定的起始位置开始)。您可以解决此问题,类似于您在建议的DP Java代码中描述它的方式。您可以将解决方案存储在整数矩阵中,其中行数对应于维数,列数为M + 1。请注意,对于每个维度,您不仅要计算制作M步骤的方式,还要计算制作M-1,M-2,...和0步骤的方式。我们需要这个信息用于第二级。
其次,计算仅使用前j个维度制作0,1,...,M步的方法的数量。假设您已经计算了仅使用第一个j-1维度制作0,1,...,M步的方法数。使用第一个j维度可以使用多少种方法来完成步骤?
假设你在尺寸j和k步骤中制作i-k步骤&lt;学家在维j中制作i-k步的方法的数量已经在第一级计算了 - 让我们将这个数量表示为a。在尺寸上进行k步的方法的数量&lt; j是递归计算的 - 让我们将这个数量表示为b。在维度j和k步骤中进行i-k步骤的方式的数量&lt;然后用a * b *(i选择k)给出j。 DP可以通过缓存计算结果来使递归更有效(在这种情况下,密钥由两部分组成:维度j和步骤数量i)。
请注意,您必须以模数1000000007执行计算。为了计算质数的二项式系数,您可能会发现此link有用。
答案 1 :(得分:3)
我认为关键在于,从任何位置,每个维度可能有0,1或2个可能的移动:
例如,在二维中你有类似的东西:
+---+---+---+---+
+ a + + + +
+---+---+---+---+
+ + + c + +
+---+---+---+---+
+ + b + + +
+---+---+---+---+
因此,您可以进行的移动总数是当前位置的移动次数加上每个新位置的移动次数。第二部分是递归。
类似下面的伪代码:
N is number of dimensions
D is array 1 to N of dimensions
X is array 1 to N of current position
int solve(array D, array X, int moves) {
// base case when no more moves possible
if (moves == 0)
return 1
// memo key is current position plus number of moves - this code is
// java and creates a string similar to "[2, 3]4"
String memoKey = Arrays.toString(X) + String.valueOf(moves);
if (hashMap.containsKey(memoKey)) {
return hashMap.get(memoKey);
}
// accumulate results from all possible moves in all dimensions
int result = 0
// check each dimension
for n = 1 to N {
// if we are not at the start of this dimension
// then add all moves for a decrease of one place
if (X[n] > 1) {
X[n] = X[n] - 1
result = result + solve(D, X, moves - 1)
X[n] = X[n] + 1
}
// if we are not at the end of this dimension
// then add all moves for a increase of one place
if (X[n] < D[n]) {
X[n] = X[n] + 1
result = result + solve(D, X, moves - 1)
X[n] = X[n] - 1
}
}
// memoise result for later
hashMap.put(memoKey, result);
return result
}
<强>更新强>
包含的记忆 - 可能不是最有效的,但它解释了一种相当简单的方法。
答案 2 :(得分:1)
N维数组可以展平为1维数组索引,即最终索引的大小为D1 * D2 * ... * DN。公式类似如下:
// this take (x1,x2,...xn) & (D1, D2, ..., DN) to produce single index.
uint64_t flatten(int N, int x[], int D[]) {
uint64_t index = 0, mult = 1;
for (int i = N-1; i>0; i--) {
index += x[i];
index *= D[i-1];
}
index += x[0];
return index;
}
// this is the reverse
void unflatten(int N, int x[], int D[], uint64_t index) {
for (int i = 0; i < N - 1; i++) {
x[i] = index % D[i];
index /= D[i];
}
x[N-1] = index;
}
使用这种方式实现N维动态编程。
答案 3 :(得分:0)
这是一个Java解决方案,通过基本上逐个计算它们来计算方法。在约8秒内计算出10亿种方式。
long compute(int N, int M, int[] positions, int[] dimensions) {
if (M == 0) {
return 1;
}
long sum = 0;
for (int i = 0; i < N; i++) {
if (positions[i] < dimensions[i]) {
positions[i]++;
sum += compute(N, M - 1, positions, dimensions);
positions[i]--;
}
if (positions[i] > 1) {
positions[i]--;
sum += compute(N, M - 1, positions, dimensions);
positions[i]++;
}
}
return sum % 1000000007;
}