welcome to my blog
LeetCode Top 100 Liked Questions 494. Target Sum (Java版; Medium)
题目描述
You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. Now you have 2 symbols + and -. For each integer,
you should choose one from + and - as its new symbol.
Find out how many ways to assign symbols to make sum of integers equal to target S.
Example 1:
Input: nums is [1, 1, 1, 1, 1], S is 3.
Output: 5
Explanation:
-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
There are 5 ways to assign symbols to make the sum of nums be target 3.
Note:
The length of the given array is positive and will not exceed 20.
The sum of elements in the given array will not exceed 1000.
Your output answer is guaranteed to be fitted in a 32-bit integer.
class Solution {
public int findTargetSumWays(int[] nums, int S) {
// x + y = sum
// x - y = S
// x = (sum + S)/2
int sum = 0;
for(int a : nums){
sum += a;
}
if(S>sum){
return 0;
}
if((sum+S)%2==1){
return 0;
}
int target = (sum + S) /2;
int[] dp = new int[target+1];
dp[0] = 1;
for(int i=0; i<nums.length; i++){
for(int j=target; j>=nums[i]; j--){
dp[j] += dp[j-nums[i]];
}
}
return dp[target];
}
}
class Solution {
public int findTargetSumWays(int[] nums, int S) {
// x + y = sum
// x - y = S
// x = (sum + S)/2
int sum = 0;
for (int a : nums) {
sum += a;
}
if(S>sum){
return 0;
}
if ((sum + S) % 2 == 1) {
return 0;
}
int target = (sum + S) / 2;
//01背包; 行是物品; 列是容量
//dp[i][j]表示以第i个元素结尾时, 有几种方式可以凑出金额j
//dp[i][j] = dp[i-1][j] 不选
//dp[i][j] = dp[i-1][j-nums[i]] 选
int[][] dp = new int[nums.length+1][target + 1];
dp[0][0] = 1;
for (int i = 1; i < nums.length+1; i++) {
for (int j = 0; j < target + 1; j++) {
dp[i][j] = dp[i - 1][j];
if (j - nums[i-1] >= 0) {
dp[i][j] += dp[i - 1][j - nums[i-1]];
}
}
}
return dp[nums.length][target];
}
}
坚持画图
第一次做;背包问题; dp[i][j]表示:前i个数组合成j的可能数量; 以后就用这种含义了! 初始化方便!!! 核心:索引和真实值的对应关系:索引值==真实值+max
/*
尝试另一种dp[i][j]的含义: 前i个数组合出j的所有可能数量;
以后碰到背包问题就用这种含义了! 原因: 初始化方便!
*/
class Solution {
public int findTargetSumWays(int[] nums, int S) {
// x + y = sum
// x - y = S
// x = (sum + S)/2
int sum = 0;
for(int a : nums){
sum += a;
}
if(S>sum){
return 0;
}
if((sum+S)%2==1){
return 0;
}
int target = (sum + S) /2;
int[] dp = new int[target+1];
dp[0] = 1;
for(int i=0; i<nums.length; i++){
for(int j=target; j>=nums[i]; j--){
dp[j] += dp[j-nums[i]];
}
}
return dp[target];
}
}
class Solution {
public int findTargetSumWays(int[] nums, int S) {
// x + y = sum
// x - y = S
// x = (sum + S)/2
int sum = 0;
for (int a : nums) {
sum += a;
}
if(S>sum){
return 0;
}
if ((sum + S) % 2 == 1) {
return 0;
}
int target = (sum + S) / 2;
//01背包; 行是物品; 列是容量
//dp[i][j]表示以nums[i]结尾时, 有几种方式可以凑出金额j
//dp[i][j] = dp[i-1][j] 不选
//dp[i][j] = dp[i-1][j-nums[i]] 选
int[][] dp = new int[nums.length][target + 1];
//初始化很细节, 需要考虑nums[0]是否为0
dp[0][0] = nums[0]==0?2:1;
if (nums[0] <= target) {
dp[0][nums[0]] = nums[0]==0?2:1;
}
for (int i = 1; i < nums.length; i++) {
for (int j = 0; j < target + 1; j++) {
dp[i][j] = dp[i - 1][j];
if (j - nums[i] >= 0) {
dp[i][j] += dp[i - 1][j - nums[i]];
}
}
}
return dp[nums.length - 1][target];
}
}
第一次做; 转换成背包问题; dp[i][j]的含义:[0,i]的数组合成j的可能数量, 初始化太细节! 不好! ;最容易错的地方有三个: 1)索引和真实值的对应 2)初始化 3)0的存在
/*
转换为背包问题
*/
class Solution {
public int findTargetSumWays(int[] nums, int S) {
//input check
if(nums==null || nums.length==0)
return 0;
int n = nums.length;
int min=0,max=0;
for(int a:nums){
max += a;
min += -a;
}
if(S>max || S<min)
return 0;
/*
dp[i][j]表示[0,i]上的数和为j的组合个数
*/
//由于索引不能为负, 所以j的实际值表示j+min这个数
int[][] dp = new int[n][max-min+1];
//初始化
// dp[n-1][0] = 1; 这句加上就错了! 没有理解清楚dp[i][j]的含义, 忽略了0的存在
//第一行
for(int j=0; j<max-min+1; j++){
//超级细节: 初始化时要单独处理0
if(nums[0]==0 && j-max==0)
dp[0][j]=2;
else if(nums[0]==j-max)
dp[0][j]=1;
else if(-nums[0]==j-max)
dp[0][j]=1;
}
//dp主体
for(int i=1; i<n; i++){
for(int j=0; j<max-min+1; j++){
//细节: 本题中对于0来说, +0和-0是不同的, 所以把0当成正数讨论即可, 这里不用单独讨论, 但是初始化时得单独讨论! 太细节了...
//不选负号
if(j-nums[i]>=0){
dp[i][j] += dp[i-1][j-nums[i]];
}
//选负号
if(j+nums[i]<max-min+1){
dp[i][j] += dp[i-1][j+nums[i]];
}
}
}
return dp[n-1][S-min];
}
}
第一次做; 暴利递归, 竟然通过了全部案例…
/*
暴利递归
*/
class Solution {
public int findTargetSumWays(int[] nums, int S) {
//input check
if(nums==null || nums.length==0)
return 0;
return core(nums, 0, S);
}
public int core(int[] nums, int index, int sum){
//base case
if(index==nums.length)
return sum==0? 1: 0;
//选负号或者不选负号
//不选
int notchoseRes = core(nums, index+1, sum-nums[index]);
//选
int choseRes = core(nums, index+1, sum+nums[index]);
return choseRes + notchoseRes;
}
}
LeetCode优秀题解; 我跟他的思路很像,但是为什么他的初始化这么简单?
/** solution 2: DP (0/1 knapsack) - Time: O(n^2), Space: O(n^2) */
/**
* sub-problem: dp[i][j] represents number of possible ways to reach sum j by using first ith items
* base case: dp[0][sum], position sum represents sum 0
* recurrence relation:
* dp[i][j] += dp[i - 1][j + nums[i - 1]] if j + nums[i - 1] <= sum * 2
* dp[i][j] += dp[i - 1][j - nums[i - 1]] if j - nums[i - 1] >= 0
*
* explanation: if j + nums[i - 1] or j - nums[i - 1] is in correct range, we can use the number nums[i - 1]
* to generate next state of dp array
* */
public int findTargetSumWays2(int[] nums, int S) {
if (nums.length == 0) {
return 0;
}
int sum = 0;
for (int num : nums) {
sum += num;
}
// corner case: when S is out of range [-sum, sum]
if (S < -sum || S > sum) {
return 0;
}
int[][] dp = new int[nums.length + 1][sum * 2 + 1];
dp[0][sum] = 1;
int leftBound = 0;
int rightBound = sum * 2;
for (int i = 1; i <= nums.length; i++) {
for (int j = leftBound; j < rightBound + 1; j++) {
// try all possible sum of (previous sum j + current number nums[i - 1]) and all possible difference of
// (previous sum j - current number nums[i - 1])
if (j + nums[i - 1] <= rightBound) {
dp[i][j] += dp[i - 1][j + nums[i - 1]];
}
if (j - nums[i - 1] >= leftBound) {
dp[i][j] += dp[i - 1][j - nums[i - 1]];
}
}
}
return dp[nums.length][sum + S];
}