题目描述
给定两个正整数N和S,你需要找出所有的长度为N的正整数数列中,满足单调递增以及总和为S的数列有多少个。
输入描述:
共一行,两个整数N和S. (1<N, s < 10000)
输出描述:
一个整数, 为满足条件的数列个数对100000007取模的结果。
分析
本题最牛逼的解法是动态规划,dp[n][s]
表示:和为s长度为n 的序列个数。状态转换方程为:
dp[n][s] = (dp[n][s-n] + dp[n-1][s-n])%1000000007;
- 其中
dp[n-1][s-n]
表示:开头数字1
的和为s
长度为n
的序列个数 dp[n][s-n]
表示:开头数字为非1
的和为s
长度为n
的序列个数s-n
是因为整个序列单调递增的,比如:
dp[2,6] 第一位数字最大不超过s/n=3
第一位数字为1时,第二位数字为1+n
其中n
为正整数,n
的情况有dp[n-1][s-n]
长度少一位,和少了第一位的1
和第二位的1
:即dp[1][6-2]
种
第一位数字为2时,第二位数字为2+n
其中n
为正整数,n
的情况有dp[n-1][s-n]
即dp[1][6-4]
种
第一位数字为3时,第二位数字为3+n
其中n
为正整数,n
的情况有dp[n-1][s-n]
即dp[1][6-6]
种
算法不是一蹴而就的,我先写出来的递归、然后写出来了迭代版本,最后才想到动态规划版本。我将三种版本的代码放到最后,供大家批判。
java代码
//动态规划版本
static int[][] dp;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNextInt()) {// 注意,如果输入是多个测试用例,请通过while循环处理多个测试用例
int n =in.nextInt();
int s = in.nextInt();
dp = new int[n+1][s+1];
Arrays.fill(dp[1],1);
dp[1][0] =0;
get(n,s);
System.out.println(dp[n][s]);
}
}
private static void get(int n,int s){
if (dp[n][s] != 0) return ;
for (int i = 2; i <= n; i++) {
for (int j = i+1; j <= s; j++) {
dp[i][j] = (dp[i][j-i] + dp[i-1][j-i])%1000000007;
}
}
}
//迭代版本
static int[][] dp;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNextInt()) {// 注意,如果输入是多个测试用例,请通过while循环处理多个测试用例
int n =in.nextInt();
int s = in.nextInt();
dp = new int[n+1][s+1];
Arrays.fill(dp[1],1);
dp[1][0] =0;
get(n,s);
System.out.println(dp[n][s]);
}
}
private static void get(int n,int s){
if (dp[n][s] != 0) return ;
for (int i = 2; i <= n; i++) {
for (int j = i+1; j <= s; j++) {
if (dp[i][j] != 0) continue;
int tem =0;
for (int k = j-i; k>=0; k=k-i ) {
tem = (tem%1000000007 + dp[i-1][k]%1000000007)%1000000007;
}
dp[i][j] = tem;
}
}
}
//递归版本
//和为s长度为n 开头大于start的序列个数
public static int dfs(int n,int s, int start){
if (n <= 1 ) {
return start < s?1:0;
}
if (dp[n][s] != 0) return dp[n][s];
int tem = 0;
//开头为i的序列个数
for (int i = start+1; i <= s/n; i++) {
tem = (tem + dfs(n-1,s - i, i))%1000000007;
}
dp[n][s] = Math.max(dp[n][s],tem)%1000000007;
return tem;
}