目標とリートコード (494)

目標とリートコード (494)

トピックリンク

トピックの説明

整数配列 nums と整数ターゲットが与えられます。

配列内の各整数に「+」または「-」を追加し、すべての整数を連結して式を作成します。

たとえば、nums = [2, 1] の場合、2 の前に「+」を追加し、1 の前に「-」を追加して、連結して式「+2-1」を取得できます。
上記のメソッドによって構築でき、ターゲットとして評価される個別の式の数を返します。

例1

入力: nums = [1,1,1,1,1]、ターゲット = 3
出力: 5
説明: 最終的なターゲットの合計を 3 にする方法は 5 つあります。
-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]、ターゲット = 1
出力: 1

制約

  • 1 <= nums.length <= 20
  • 0 <= nums[i] <= 1000
  • 0 <= sum(nums[i]) <= 1000
  • -1000 <= ターゲット <= 1000

アイデア (動的プログラミング)

アイデア 1

dp 配列は 2 次元であり、dp[i][j] は、最初の i 個の数値を組み合わせて j を生成するときに考えられるすべての種を表します。

j を構成する最初の i 数値は、最初の i - 1 数値形式 (j - nums[i]) プラス nums[i]、または最初の i - 1 数値形式 (j + nums[i]) マイナス nums と見なすことができます [私]

したがって、状態遷移方程式dp[i][j] = dp[i - 1][j - nums[i]] + dp[i - 1][j + nums[i]]


nums[i] の値の範囲は [0, 1000]、target の値の範囲は [-1000, 1000] です。

nums[i] = 1000 の場合、dp[i][-1000] は dp[i-1][-2000] と dp[i-1][0] で構成できます。

dp[i][1000]は、dp[i-1][0]とdp[i-1][2000]から構築できます。

つまり j の範囲は -2000 から 2000 です

配列が範囲外になるのを防ぐために、 int[][] dp = new int[nums.length][4001] とします。


コード1

class Solution {
    
    
     // 0,1 背包是取和不取 这道题是 取正和取负
    public int findTargetSumWays(int[] nums, int target) {
    
    
        // dp的第二维 取值范围为[-2000,2000]跨度为4000
        int[][] dp = new int[nums.length][4001]; // dp[i][j] 表示前i个物品组合成j+2000的可能性的种数
        // 状态转移方程感觉是个递归 dp[i][j] = dp[i-1][j-nums[i]] + dp[i-1][j+nums[i]]
        dp[0][nums[0] + 2000] += 1;
        dp[0][-nums[0] + 2000] += 1; //因为如果是0 的话 取正取负 和为0就有两种
        for (int i = 1; i < nums.length; i++) {
    
    
            for (int j = nums[i]; j<=4000-nums[i]; j++) {
    
    //这里要注意判别,防止越界
                dp[i][j] = dp[i - 1][j - nums[i]] + dp[i - 1][j + nums[i]];
            }
        }
        return dp[nums.length - 1][target + 2000];
    }
}

注: コードの 8 ~ 9 行目では、= の代わりに += を使用してください。nums[i] =0 の場合、正も負も 0 になりますが、方法は 2 つあります。


アイデア 2

それぞれの番号に対して 2 つのオプションがあります。(1) 符号を正にする (2) 符号を負にする

したがって、選択に従って 2 つの山に分けることができます。

正として選択された一連の数値の合計を pos とし、負として選択された一連の数値の合計を neg とします。

すべての数値の合計が sum で、ターゲットが target です。

次の式があります:
{ neg + pos = sum ( 1 ) pos − neg = target ( 2 ) \left\{ \begin{aligned} neg +pos & = sum &(1)\\ pos - neg & = target &(2) \\ \end{aligned} \right。{ いいえ_ _+良い_ _良い_ _いいえ_ _=うーん_ _=ターゲット_ _ _ _ _( 1 )( 2 )
用(1)+(2)得2 ∗ pos = sum + target 2 *pos = sum + target2良い_ _=うーん_ _+ターゲット_ _ _ _ _

したがって、この問題を、数値の束から任意の数値を取り出して、その合計が( sum + target ) / 2 (sum + target)/2 になるように変えることができます。(うーん_ _+ターゲット) / 2 . _ _ _ _ _ 方法は全部で何通りありますか?

コード2

class Solution {
    
    
    public int findTargetSumWays(int[] nums, int target) {
    
    
        int sum = 0;
        for (int num : nums) {
    
    
            sum += num;
        }
        if ((target + sum) % 2 != 0) return 0; // target + sum 不能被 2 整除,表示无法选数构成pos

        int pos = Math.abs((sum + target) / 2);
        int[] dp = new int[pos + 1]; // dp[i]表示 从中 nums中取任意个数,能构成 i 的种数
        dp[0] = 1;
        for (int i = 0; i < nums.length; i++) {
    
    
            for (int j = pos; j >= nums[i]; j--) {
    
    
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[pos];
    }
}

おすすめ

転載: blog.csdn.net/jiaweilovemingming/article/details/124388345