2019 拼多多校招第三题sum 服务端研发工程师

题目描述

给定两个正整数N和S,你需要找出所有的长度为N的正整数数列中,满足单调递增以及总和为S的数列有多少个。

输入描述:

共一行,两个整数N和S. (1

输出描述:

一个整数, 为满足条件的数列个数对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;
    }
  

你可能感兴趣的:(2019 拼多多校招第三题sum 服务端研发工程师)