【动态规划】数组部分和相关问题

求数组的部分和,以及相关问题,最容易想到的是回溯算法,也就是深度优先搜索,这个算法的复杂度一向比较高,O(2^n),在online judge平台上往往和超时相伴。今天我的一个同学做自己跳动公司的笔试题,就遇到了这类题。

题目是:给定一个数组nums和一个目标值m,可以在数组里不重复地选择任意个数字,输出这些数字的和对m取余的最大值。

这道题出在4道笔试题里的第4题,一般来说会有以下几个特点:1.略难;2.时间不太够了;3.脑子有点昏。我同学知道回溯肯定超时,于是选择了放弃,但在放弃之前,他简单地返回了 m-1,获得了20%的分数,真是机智如我。但他笔试完后还是没有思路,觉得这是一道很有难度的题。我也没怎么见过类似的题,简单搜索之后,发现可以用0-1背包的思路做数组部分和的题。选中的就是1,没选中的就是0。话不多说,上代码:

    // 求数组部分和为 k 的不同方案数
    public int partSum(int[] nums, int k) {
        int n = nums.length;
        int[] dp = new int[k+1];
        dp[0] = 1;

        for (int i = 0; i < n; i++) {
            for (int j = k; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[k];
    }
    // 求数组部分和对 m 取余的最大值
    public int partSum2(int[] nums, int m) {
        int n = nums.length;
        boolean[] dp = new boolean[m];
        dp[0] = true;
        
        for (int i = 0; i < n; i++) {
            for (int j = m-1; j >= 0; j--) {
                int index = getIndex(j - nums[i], m);
                dp[j] |= dp[index];
            }
        }

        for (int j = m-1; j >= 0; j--) {
            if (dp[j])
                return j;
        }
        return 0;
    }

    private int getIndex(int k, int m) {
        int res = k % m;
        if (res < 0)
            res += m;
        return res;
    }
posted @ 2020-08-16 15:54  y1x4  阅读(292)  评论(0)    收藏  举报