LeetCode:返回可以使得数组的和等于目标数S的所有添加符号的方法数

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))
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路上的追梦人

您的鼓励就是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值