输出包括一行四个正整数N(2<=N<=5000)、M(1<=M<=N)、K(1<=K<=5000)、P(1<=P<=N)。
输出一个整数,代表最终走到P的方法数对取模后的值。
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
1000 1 1000 1
591137401
注意答案要取模
时间复杂度,空间复杂度
。
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] params = br.readLine().split(" "); int n = Integer.parseInt(params[0]); int s = Integer.parseInt(params[1]); int k = Integer.parseInt(params[2]); int e = Integer.parseInt(params[3]); System.out.println(recurrent(n, s, k, e)); } private static int recurrent(int n, int cur, int rest, int end) { if(rest == 0){ // 没有步数了,如果到达了目的地,就生成了一种走法,否则走法无效 return cur == end? 1: 0; }else if(cur == 1){ // 到达左边界,只能往右走,步数减1 return recurrent(n, cur + 1, rest - 1, end, memory) % 1000000007; }else if(cur == n){ // 到达右边界,只能往左走,步数减1 return recurrent(n, cur - 1, rest - 1, end, memory) % 1000000007; }else{ // 中间位置可以往两边走 return (recurrent(n, cur - 1, rest - 1, end, memory) + recurrent(n, cur + 1, rest - 1, end, memory)) % 1000000007; } } }这种递归的做法逻辑还是很清晰的,可以大体上视为每一步都有两种选择,一共有k步,时间复杂度就是2k指数级别,当k很大时会产生指数爆炸,从而超时。假设递归函数为f,仅传入两个变化的参数cur和rest(n,start和end是固定的,这里省略),假设n=5,start=2,k=4,我们要求的是f(2,,4),就会得到如下图的展开:
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] params = br.readLine().split(" "); int n = Integer.parseInt(params[0]); int s = Integer.parseInt(params[1]); int k = Integer.parseInt(params[2]); int e = Integer.parseInt(params[3]); // 初始状态改为无意义的-1 int[][] memory = new int[k + 1][n + 1]; for(int i = 0; i <= k; i++){ for(int j = 0; j <= n; j++) memory[i][j] = -1; } System.out.println(recurrent(n, s, k, e, memory)); } private static int recurrent(int n, int cur, int rest, int end, int[][] memory) { // 有记忆,直接返回 if(memory[rest][cur] != -1) return memory[rest][cur]; if(rest == 0){ // 没有步数了,如果到达了目的地,就生成了一种走法,否则走法无效 memory[rest][cur] = cur == end? 1: 0; }else if(cur == 1){ // 到达左边界,只能往右走,步数减1 memory[rest][cur] = recurrent(n, cur + 1, rest - 1, end, memory) % 1000000007; }else if(cur == n){ // 到达右边界,只能往左走,步数减1 memory[rest][cur] = recurrent(n, cur - 1, rest - 1, end, memory) % 1000000007; }else{ // 中间位置可以往两边走 memory[rest][cur] = (recurrent(n, cur - 1, rest - 1, end, memory) + recurrent(n, cur + 1, rest - 1, end, memory)) % 1000000007; } return memory[rest][cur]; } }仍然是递归,但由于在递归函数的开头就判断了记忆表中是否有f(cur,rest),所以这棵树被进行了大量的剪枝,其实只是不断在填表而已,表的大小是(k+1)*(n+1),因此时间复杂度为O(k*n),以O(k*n)的空间节省了递归展开的时间。
import scala.io.StdIn object Main { def main(args: Array[String]): Unit = { val in = StdIn val params = in.readLine().split(" ") val n = params(0).toInt val start = params(1).toInt val k = params(2).toInt val end = params(3).toInt val dp = Array.ofDim[Int](k + 1, n + 1) dp(0)(end) = 1 // 剩下0步,到达了end,根据递归函数,直接出来一种方案 // 填表,注意第一列是废的 for(rest <- 1 to k; cur <- 1 to n){ if(cur == 1) dp(rest)(cur) = dp(rest - 1)(cur + 1) % 1000000007 else if(cur == n) dp(rest)(cur) = dp(rest - 1)(cur - 1) % 1000000007 else dp(rest)(cur) = (dp(rest - 1)(cur - 1) + dp(rest - 1)(cur + 1)) % 1000000007 } println(dp(k)(start)) } }
import java.util.*; public class Main { public static final int mod = 1_000_000_007; public static int process(int N, int M, int K, int P) { int[] dp = new int[N + 1]; dp[P] = 1; for (int k = 1; k <= K; k++) { int upLeft = dp[1]; for (int i = 1; i <= N; i++) { int tmp = dp[i]; if (i == 1) dp[i] = dp[i + 1]; else if (i == N) dp[i] = upLeft; else dp[i] = (upLeft + dp[i + 1]) % mod; upLeft = tmp; } } return dp[M]; } public static void main(String[] args) { Scanner sc = new Scanner(System.in); int N = sc.nextInt(); // (2<=N<=5000) int M = sc.nextInt(); int K = sc.nextInt(); // (1<=K<=5000) int P = sc.nextInt(); int res = process(N, M, K, P); System.out.println(res); } }
import java.util.Scanner; public class Main{ public static void main(String[] args){ Scanner sc = new Scanner(System.in); int N = sc.nextInt(); int M = sc.nextInt(); int K = sc.nextInt(); int P = sc.nextInt(); System.out.println(search(N, M, K, P)); } public static int search(int N, int M, int K, int P){ if(N < 2 || K <1 || M < 1 || M > N || P <1 ||P > N){ return 0; } int[][] dp = new int[K+1][N+1]; dp[0][P] = 1; for(int i = 1; i <= K; i++){ for(int j = 1; j <= N; j++){ if(j==1){ dp[i][j] = dp[i-1][2] % (int)(1e9+7); }else if(j == N){ dp[i][j] = dp[i-1][N-1] % (int)(1e9+7); }else{ dp[i][j] = (dp[i-1][j-1]+dp[i-1][j+1])%(int)(1e9+7); } } } return dp[K][M] % (int)(1e9+7); } }