1. 程式人生 > 其它 >微信小程式wx.getBackgroundAudioManager()背景音訊播放

微信小程式wx.getBackgroundAudioManager()背景音訊播放

494. 目標和

題目連結:494. 目標和(中等)

給你一個整數陣列 nums 和一個整數 target

向陣列中的每個整數前新增 '+''-' ,然後串聯起所有整數,可以構造一個 表示式

  • 例如,nums = [2, 1] ,可以在 2 之前新增 '+' ,在 1 之前新增 '-' ,然後串聯起來得到表示式 "+2-1"

返回可以通過上述方法構造的、運算結果等於 target 的不同 表示式 的數目。

示例 1:

輸入:nums = [1,1,1,1,1], target = 3
輸出:5
解釋:一共有 5 種方法讓最終目標和為 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3

示例 2:

輸入:nums = [1], target = 1
輸出:1

提示:

  • 1 <= nums.length <= 20

  • 0 <= nums[i] <= 1000

  • 0 <= sum(nums[i]) <= 1000

  • -1000 <= target <= 1000

解題思路

假設符號為+的整數(非負數)總和為left,符號為-的整數(非負數)總和為right,那麼有公式target = left - right以及sum = left + right。根據這兩個公式,可以得到2*left = target + sumleft = (target + sum) / 2

於是題目就可以轉換為:裝滿容量為left的揹包,最多有幾種方法。這可以用01揹包來解。

首先,我們要兩種情況需要先排除:

  • target的絕對值大於sum時,不可能實現

  • 從公式2*left = target + sum可以看出,2*left一定時偶數,所以target + sum也必須保證時偶數。

使用一維陣列的解題步驟:

  1. 確定dp陣列以及其下標的含義

    dp[j]表示裝滿揹包容量為j的有多少種方法

  2. 確定遞推公式

    不考慮nums[i]的情況下,填滿容量為j - nums[i]的揹包,有dp[j - nums[i]]種方法。

    也就是當前填滿容量為j的揹包的方法數 = 之前填滿容量為j

    的揹包的方法數 + 之前填滿容量為j - nums[i]的方法數。於是得到遞推公式為:dp[j] = dp[j] + dp[j - nums[i]]

  3. dp陣列的初始化

    用0件物品(在下面的程式碼中可以看到遍歷物品的i是從下標0開始的,所以此時並沒有物品)裝滿揹包容量為0的揹包,有一種方法。

    所以dp[0] = 1

  4. 確定遍歷順序

    一維dp解決0-1揹包問題,先正序遍歷物品,再倒敘遍歷揹包。

  5. 舉例推導dp陣列(略)

C++

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for (int i = 0; i < nums.size(); i++) {
            sum += nums[i];
        }
        if (abs(target) > sum || ( target + sum ) % 2 == 1) {
            return 0;
        }
        int bagSize = ( sum + target ) / 2;
        vector<int> dp(bagSize + 1, 0);
        dp[0] = 1;
        for (int i = 0; i < nums.size(); i++) {
            for (int j = bagSize; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[bagSize];
    }
};

JavaScript

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var findTargetSumWays = function(nums, target) {
    let sum = 0;
    for (let i = 0; i < nums.length; i++) {
        sum += nums[i];
    }
    if (Math.abs(target) > sum || (sum + target) % 2 == 1) {
        return 0;
    }
    let bagSize = (sum + target) / 2;
    const dp = new Array(bagSize + 1).fill(0);
    dp[0] = 1;
    for (let i = 0; i < nums.length; i++) {
        for (let j = bagSize + 1; j >= nums[i]; j--) {
            dp[j] += dp[j - nums[i]];
        }
    }
    return dp[bagSize];
};
  • 時間複雜度:O(n × m),n為正數個數,m為揹包容量

  • 空間複雜度:O(m)

另外,還可以用二維陣列解決。不過需要注意dp陣列的初始化,因為nums[0] = 0是一種特殊的情況。給出C++程式碼如下。

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for (int i = 0; i < nums.size(); i++) {
            sum += nums[i];
        }
        if (abs(target) > sum || ( target + sum ) % 2 == 1) {
            return 0;
        }
        int bagSize = ( sum + target ) / 2;
        vector<vector<int>> dp(nums.size(),vector<int>(bagSize + 1, 0));
        // 特殊!!!!!!!!!!!!!!!!!!!!!!
        // 因為 0 既可以用 -0 表示, 也可以用 +0 表示
        // 所以將 0 (不是物品的下標,而是物品的重量或價值) 填滿容量揹包為 0 的揹包中,有兩種方法
        if (nums[0] == 0) {
            dp[0][0] = 2;
        } else {
            dp[0][0] = 1;
        }
        for (int j = 1; j < bagSize + 1; j++) {
            if (j == nums[0]) dp[0][j] = 1;
        }
        for (int i = 1; i < nums.size(); i++) {
            for (int j = 0; j < bagSize + 1; j++) {
                if (j < nums[i]) {
                    dp[i][j] = dp[i - 1][j];
                } else {
                    dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]];
                }
            }
        }
        return dp[nums.size() - 1][bagSize];
    }
};