数字和为sum的方法数

链接:https://www.nowcoder.com/questionTerminal/7f24eb7266ce4b0792ce8721d6259800
来源:牛客网

给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数。
当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案。

输入描述:

输入为两行:
第一行为两个正整数n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000)
第二行为n个正整数Ai,以空格隔开。

输出描述:

输出所求的方案数

示例1
输入

5 15 5 5 10 2 3

输出

4

我刚开始做这道题首先想到的是递归,但是最后测了以下发现递归的时间复杂度太大了,只能通过50%。所以这道题正确的解法应该是使用动态规划。
动态规划也挺烧脑的,这里的思路是:创建二维数组dp[][],依次计算数组arr[]中的元素可以组成0-sum之间数字的可能情况。比如数组元素为5 5 10 2 3,首先计算5在0-sum之间的可能情况,其中5可以组合成5,其他都不可以,所以dp[1][5] = 1;接着加入下一个元素5,这时可以组成的5的可能情况就是两种,分别是第一个5和第二个5,而且这时还可以组成10,所以dp[2][5] = 2,dp[2][10] = 1;以此类推就可以算出来当加入元素有5 5 10 2 3时可以组成0-sum之间数字的可能情况,当然最终要求解的只是组成sum的情况数,所以输出dp[n][sum]就可以了。
使用5 5 10 2 3来举例,二维图如下:
在这里插入图片描述
这里的dp[1][5] = 1,就是因为现在只有1个5。而dp[2][5] = 2是因为有两个5,并且这两个5可以组成10,所以dp[2][10] = 1;
可以发现一个规律,
dp[i][j]表示用前i个值组成和为j的方法数
当j < arrp[i]时,dp[i][j] = dp[i -1][j]
当j >= arr[i]时,dp[i][j] = dp[i - 1][j] + dp[i - 1][j - arr[i]]
这是为什么呢?是因为当j < arrp[i]时,这时就算加入新的元素,但是新的元素比要组成的j大,所以情况就和没有加入新的元素时一样。而当j >= arr[i]时,这时候新加入的元素就有用了,情况也就发生了改变,比如现在新加入了2,那么要组成15,就等于之前可以组成15的情况+现在加入2后可以组成15的情况,加入2后可以组成15的情况就可等同于之前可以组成13的情况,因为13 + 2= 15,有多少种13的组成情况就有多少种15的组成情况。核心思路就是上面的过程。
这里还有一些小细节:
比如,第一列为1,是因为dp[i][0],每个元素都可以满足0,而第一行为0,是为了之后的动态规划做准备。

程序如下:


import java.util.Scanner;

/**
 * @author abaka
 * @date 2019/7/7 14:37
 */
public class AddEqualSum {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int sum = in.nextInt();
        int[] arr = new int[n + 1];
        long[][] dp = new long[n + 1][sum + 1];
        for (int i = 0; i <= n;i++){
            dp[i][0] = 1;
        }
        for (int j = 1; j <= sum; j++){
            dp[0][j] = 0;
        }
        for (int i = 1; i <= n; i++){
            arr[i] = in.nextInt();
            for (int j = 0; j <= sum; j++){
                if (j >= arr[i])
                    dp[i][j] = dp[i - 1][j] + dp[i - 1][j - arr[i]];
                else
                    dp[i][j] = dp[i - 1][j];
            }
        }

        System.out.println(dp[n][sum]);
    }


//下面这是一种优化方法,将二维数组的动态规划优化为一维数组,
    //关键点是在内层for循环中,这里是从后向前循环的,所以可以使用一维数组。
    //因为j < arr[i]的情况和之前的情况一样,所以就直接使用一维数组中之前所保存的数
//    public static void main(String[] args) {
//        Scanner in = new Scanner(System.in);
//        int n = in.nextInt();
//        int sum = in.nextInt();
//        int[] value = new int[n];
//        long[] dp = new long[sum + 1];
//        dp[0] = 1;
//        for (int i = 0; i < n; i++){
//            value[i] = in.nextInt();
//            for (int j = sum; j >= 0; j--){
//                if (j >= value[i]){
//                    dp[j] += dp[j - value[i]];
//                }
//            }
//        }
//        System.out.println(dp[sum]);
//    }

}

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值