假设有排成一行的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
输入
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
说明
注意答案要取模
################################################################################
解题思路一:暴力递归
public static int ways1(int N,int M,int K,int P) {
if(N<2||K<1)return 0;
return walk1(N,M,K,P);
}
public static int walk1(int N,int cur,int rest,int P) {//rest:剩余的步数 cur:当前的位置
if(rest==0)return cur==P?1:0;
if(cur==1) {
return walk1(N,cur+1,rest-1,P);
}
if(cur==N) {
return walk1(N,cur-1,rest-1,P);
}
return (walk1(N,cur-1,rest-1,P)+walk1(N,cur+1,rest-1,P))%1000000007;
}
暴力递归改动态规划的目的只有一个:以空间换取时间,因为暴力递归细分会出现好多重复的暴力展开,浪费了时间,暴力递归改动态规划就是将已经暴力展开的子情况的值记录下来,下次再次遇到相同的子情况就不用再次浪费时间去做暴力展开了,直接用值就行了。
解题思路二:暴力递归改动态规划 1
借助傻缓存cache(哈希表实现),记忆化搜索。
public static int ways2(int N,int M,int K,int P) {
if(N<2||K<1)return 0;
HashMap<String,Integer>cache=new HashMap<>();
return walk2(N,M,K,P,cache);
}
public static int walk2(int N,int cur,int rest,int P,HashMap<String,Integer>cache) {//rest:剩余的步数 cur:当前的位置
String key=String.valueOf(cur)+"_"+String.valueOf(rest);
if(cache.containsKey(key))return cache.get(key);
int ans=0;
if(rest==0) {
ans=cur==P?1:0;
cache.put(key, ans);
return ans;
}
if(cur==1) {
ans=walk2(N,cur+1,rest-1,P,cache);
cache.put(key, ans);
return ans;
}
if(cur==N) {
ans=walk2(N,cur-1,rest-1,P,cache);
cache.put(key, ans);
return ans;
}
ans=(walk2(N,cur-1,rest-1,P,cache)+walk2(N,cur+1,rest-1,P,cache))%1000000007;
cache.put(key, ans);
return ans;
}
解题思路三:暴力递归改动态规划 2
哈希表虽然操作都是O(1),但是常数时间还是比较大的,可以采用二维数组的方法来记录。
public static int ways3(int N,int M,int K,int P) {
if(N<2||K<1)return 0;
int[][] cache=new int[N+1][K+1];
//将数组初始化为-1,表示还没求过这个值
for(int i=1;i<cache.length;++i) {
for(int j=0;j<cache[0].length;++j) {
cache[i][j]=-1;
}
}
return walk3(N,M,K,P,cache);
}
public static int walk3(int N,int cur,int rest,int P,int[][] cache) {//rest:剩余的步数 cur:当前的位置
if(cache[cur][rest]!=-1)return cache[cur][rest];
int ans=0;
if(rest==0) {
ans= cur==P?1:0;
cache[cur][rest]=ans;
return ans;
}
if(cur==1) {
ans=walk3(N,cur+1,rest-1,P,cache);
cache[cur][rest]=ans;
return ans;
}
if(cur==N) {
ans=walk3(N,cur-1,rest-1,P,cache);
cache[cur][rest]=ans;
return ans;
}
ans=(walk3(N,cur-1,rest-1,P,cache)+walk3(N,cur+1,rest-1,P,cache))%1000000007;
cache[cur][rest]=ans;
return ans;
}
解题思路四:暴力递归改动态规划 3
可变的参数K和M,填下面的表:
N=7,M=2,K=5,P=3
先确定初始条件,第一列的第M行为1,其他为0
任何列的第一行都只依靠其左下角的值
任何列的最后一行都只依靠其左上角的值
其他位置同时依靠其左上角和左下角的值
结果就是[M][K]的值
public static int waysDP(int N,int start,int K,int end) {
if(N<2||start<1||end>N||K<1||start>N||end<1)return 0;
int[][] dp=new int[N+1][K+1];
dp[end][0]=1;
for(int col=1;col<=K;++col) {
dp[1][col]=dp[2][col-1];
dp[N][col]=dp[N-1][col-1];
for(int row=2;row<N;++row) {
dp[row][col]=(dp[row-1][col-1]+dp[row+1][col-1]) % 1000000007;
}
}
return dp[start][K];
}