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=[1,1,1,1,1],S=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:
1.The length of the given array is positive and will not exceed 20.
2.The sum of elements in the given array will not exceed 1000.
3.Your output answer is guaranteed to be fitted in a 32-bit integer.
题目大意:
给定一个非负整数数组nums和一个目标数S.给出符号"+"和"-",对于数组中的任意一个整数,可以从这两种符号
中任选其中一个放在其前面.返回可以使得数组的和等于目标数S的所有添加符号的方法数.
解题思路:
方法1:
利用dfs深度搜索算法,每次遍历每个元素的正负值,递归退出条件,大于数组长度或者等于数组长度时此时目标值不为零,进行
增加1操作是目标值为零且等于数组长度.需要注意的是遍历数组某个元素时,需要判断当前k值是否小于数组长度,以免越界.
方法2:
dfs+剪枝优化
方法3:
动态规划
题目要求数组的元素加上+和-,其实相当于把数组分成了2组,一组全部都加+号,另外一组全部加-号.则存在如下关系:
sum(P)-sum(N)=target,两边同时加上sum(P)+sum(N),则
sum(P)+sum(N)+sum(P)-sum(N)=target+sum(P)+sum(N)
因此2*sum(P)=target+sum(nums),那么这道题就转换成了,能否在数组中找到这样一个集合,和等于
(target+sum(nums))/2.其中dp[i]存储的是能使和为i的方法个数.
若和不是偶数,即不能被2整除,直接输出0.
Go语言实现
】
package main
import "fmt"
//dfs
func findTargetSumWays(nums []int ,S int)int{
ans:=0
dfs(nums,S,0,&ans)
return ans
}
func dfs(nums []int,S,k int,ans *int){
if k>len(nums)||(S!=0&&k==len(nums)){//递归返回
return
}
if S==0&&k==len(nums){
*ans=*ans+1
return
}
if k<len(nums){//保证索引不越界
board:=[]int{-nums[k],nums[k]}
for i:=0;i<2;i++{
S-=board[i]
dfs(nums,S,k+1,ans)
S+=board[i]
}
}
}
//dfs+剪枝优化
func findTargetSumWaysOne(nums []int,S int)int{
//sums[i]存储的是后缀和nums[i:],即从i到结尾的和,进行剪枝优化
sums:=make([]int,len(nums))
sums[len(nums)-1]=nums[len(nums)-1]
for i:=len(nums)-2;i>=0;i--{
sums[i]=sums[i+1]+nums[i]
}
ans:=0
dfsOne(nums,0,0,S,&ans,sums)
return ans
}
func dfsOne(nums []int,index,curSum,S int,ans *int,sums []int){
if index==len(nums){
if curSum==S{
*ans=*ans+1
}
return
}
//剪枝优化:若sums[index]的值小于剩下需要正数的值,那么右边就算都是+号也无能为力
if S>curSum+sums[index]{
return
}
dfsOne(nums,index+1,curSum+nums[index],S,ans,sums)
dfsOne(nums,index+1,curSum-nums[index],S,ans,sums)
}
//动态规划
func findTargetSumWaysTwo(nums []int,S int)int{
total:=0
for _,n:=range nums{
total+=n
}
if S>total||(S+total)%2==1{
return 0
}
target:=(S+total)>>1
dp:=make([]int,target+1)
dp[0]=1
for _,j:=range nums{
for i:=target;i>=j;i--{
dp[i]+=dp[i-j]
}
}
return dp[target]
}
func main(){
nums,S:=[]int{1,1,1,1,1},3
fmt.Println(findTargetSumWays(nums,S))
fmt.Println(findTargetSumWaysOne(nums,S))
fmt.Println(findTargetSumWaysTwo(nums,S))
}