hiho212 Target Sum

题目1 : Target Sum

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

There is a sequence of N positive integers A1, A2, ... AN and a target sum S. You need to add a plus or minus operator (+/-) before every integer so that the resulted expression equals to S. Find out the number of such expressions.

Example if the sequence A is [1, 1, 1, 1, 1] and the target sum S is 3. There are 5 valid expressions: -1+1+1+1+1=3, +1-1+1+1+1=3, +1+1-1+1+1=3, +1+1+1-1+1=3 and +1+1+1+1-1=3.

输入

The first line contains 2 integers N and S denoting the number of integers and the target sum.  

The second line contains N integers A1, A2, ... AN.

For 80% of the data: 1 <= N <= 10

For 100% of the data: 1 <= N <= 100 1 <= S <= 100000 1 <= Ai <= 1000

输出

The number of valid expressions. The answer may be very large so you only need to output the answer modulo 1000000007.

样例输入

5 3
1 1 1 1 1

样例输出

5

题意分析:

1.题是什么?

    给你n个数字的排列,每个数字前面都必须添加一个加号或者减号,添加好之后把它看作一个算式计算值,问你值为sum的符号填写方案有几种?(取模1000000007)

2.思路

(1).普遍思路

    像这种方案的一看就应该反应过来是DP,普通思路的话就是一个线性的DP,具体看这儿就好 hiho212讨论

(2).我的思路

    我自己第一时间也是想到的普通思路,不过因为前方的求和结果可能为负数,故而DP时直接做值到下标的映射挺麻烦的,还必须用滚动数组,另外复杂度方面也会被固定为n*值域大小,偷懒的我选择去尝试简化问题:

    问题其实就是让你做决策哪些位置该放加号哪些位置该放减号,然后由于每个位置都必须放而且只能是加减号,故而我假设一开始每个位置都是加号,我要做的只是判断我该把哪些变为减号使之和满足条件,有多少种.

    我们首先得到一个重要的数值gap:每个符号都是+时也就是所有数字的和与目标sum的差值

    其实原本不满足变为满足的过程就是将某些加号变减号使gap逐渐缩小直至0的过程.而位置i前面的符号由+变为-对gap的影响就是-2*a[i],故而问题被我们转化成从n个数字中选取某些数字,这些数字的二倍的和等于gap则此为一个满足条件的情况,说白了就是要找我需要选哪些数字才能填平gap,填平了那肯定这是满足条件的.

    问题变成了这样子,滚动数组什么的都不需要了,其实就是n个数字中选部分数字使之和为gap的组数,记住对数字取个二倍.

(3).小细节

    gap一定要先判断一下是不是小于0的,否则会bug.

    虽然不需要滚动数组,但一定要记住递推时要for(int j=gap;j>=2*a[i];j--),这样才能正确的覆盖进行DP.

ac代码:

#include <stdio.h>
typedef long long ll;
const int maxn=100005;
const ll mod=1000000007;
int a[100]; 
ll dp[maxn]; //dp[i]表示当前和为i的结果种数 

int main(){
	int n,sum;
	scanf("%d%d",&n,&sum);
	for(int i=0;i<n;i++) scanf("%d",&a[i]);
	
	int gap=-sum; //gap为n个数的和减去sum
	for(int i=0;i<n;i++) gap+=a[i];
	
	if(gap<0) printf("0");
	else{
		for(int i=0;i<maxn;i++) dp[i]=0;
		dp[0]=1; //初始只有和为0这么一个情况,堆空间初始化好就全是0,可是安全起见还是初始化一下 
		for(int i=0;i<n;i++) for(int j=gap;j>=2*a[i];j--) dp[j]=(dp[j]+dp[j-2*a[i]])%mod; //核心递推,其实就是求解从n个数字中取和为gap的组合数 
	    printf("%lld",dp[gap]);
	}                                          
	return 0; 
}

猜你喜欢

转载自blog.csdn.net/qq_31964727/article/details/81150534