목표 및 leetcode (494)
주제 설명
정수 배열 숫자와 정수 대상이 주어집니다.
배열의 각 정수에 '+' 또는 '-'를 추가한 다음 모든 정수를 연결하여 식을 구성합니다.
예를 들어 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
입력: 숫자 = [1], 대상 = 1
출력: 1
강제
- 1 <= 숫자.길이 <= 20
- 0 <= 숫자[i] <= 1000
- 0 <= 합계(숫자[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
각 번호에 대해 두 가지 옵션이 있습니다. (1) 부호를 양수로 만들기 (2) 부호를 음수로 만들기
따라서 우리의 선택에 따라 두 더미로 나눌 수 있습니다.
양수로 선택한 수 묶음의 합을 pos로 하고, 음수로 선택한 수 묶음의 합을 음수로 합니다.
모든 숫자의 합은 합계이고 대상은 대상입니다.
다음과 같은 표현이 있습니다:
{ neg + pos = sum ( 1 ) pos − neg = target ( 2 ) \left\{ \begin{aligned} neg +pos & = sum &(1)\\ pos - neg & = target &(2) \\ \end{aligned} \right.{
부정 _ _+p o sp o s-응 _ _=음 음 _=목표 _ _ _ _ _( 1 )( 2 )
用(1)+(2)得2 ∗ pos = sum + target 2 *pos = sum + target2∗p o s=음 음 _+목표 _ _ _ _ _
따라서 이 문제를 여러 숫자에서 임의의 숫자를 취하여 합이 ( sum + target ) / 2 (sum + target)/2가 되도록 할 수 있습니다.( 음 _ _+t a r g e t ) / 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];
}
}