蓝桥杯:波动数列

问题描述
  观察这个数列:
  1 3 0 2 -1 1 -2 ...

  这个数列中后一项总是比前一项增加2或者减少3。

  栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加a或者减少b的整数数列可能有多少种呢?
输入格式
  输入的第一行包含四个整数 n s a b,含义如前面说述。
输出格式
  输出一行,包含一个整数,表示满足条件的方案数。由于这个数很大,请输出方案数除以100000007的余数。
样例输入
4 10 2 3
样例输出
2
样例说明
  这两个数列分别是2 4 1 3和7 4 1 -2。
数据规模和约定
  对于10%的数据,1<=n<=5,0<=s<=5,1<=a,b<=5;
  对于30%的数据,1<=n<=30,0<=s<=30,1<=a,b<=30;
  对于50%的数据,1<=n<=50,0<=s<=50,1<=a,b<=50;
  对于70%的数据,1<=n<=100,0<=s<=500,1<=a, b<=50;
  对于100%的数据,1<=n<=1000,-1,000,000,000<=s<=1,000,000,000,1<=a, b<=1,000,000。

Thinking:
设:有数组a[n],首项为a[0]
依题意有:a[0]+a[1]+....a[n]=s;
易知,a[0]的下限为:
a[0]+(a[0]+a)+(a[0]+2*a)+....+(a[0]+(n-1)*a)=s 
<==> n*a[0]+(n*(n-1)/2)*a=s;
<==> a[0]=(s- (n*(n-1)/2)*a)/n; 此时得到a[0]的下限.
a[0]的最大值为  
a[0]+(a[0]-b)+(a[0]-2*b)+....+(a[0]+(n-1)*b)=s  
<==>n*a[0]-(n*(n-1)/2)*b=s;
<==>a[0]=(s+(n*(n-1)/2)*b)/n; 此时得到a[0]的上限.
综上得到首项a[0]的范围:
(s- (n*(n-1)/2)*a )/n <= a[0] <= (s+(n*(n-1)/2)*b)/n 
设items=n*(n-1)/2,x为a的个数,y为b的个数,items是a的个数加上b的个数的和,则有:
x+y==items
这样写完之后发现结果不通过,思考之后发现,很有可能是x个a,和y个b的排列顺序不一致,导致得到的结果少了,
所以之后加了一个判断函数more();
再去提交,结果对了,有部分超时,80%的数据出错,我就想,是不是int overRange了,所以把所有的int换成了longlong(其实没必要把所有的都换),之后所有数据都通过了,但是超时.如果把dfs()部分,换成dp,应该快很多,先这样.

#include<iostream>
#define module 100000007
using namespace std;
long long  tempCount;
void dfs(long long  step,long long  a,long long  b,long long  temp,long long  sum,long long  s,long long  n)
{
	if(step>=n)
		return;
	else if(step==n-1)
	{
		if(sum==s)
			tempCount++;
		return;
	}
	
	temp+=a;
	dfs(step+1,a,b,temp,sum+temp,s,n);
	temp-=a;
	
	temp-=b;
	dfs(step+1,a,b,temp,sum+temp,s,n);
	temp+=b;
}
long long  more(long long  a1,long long  a,long long  b,long long  x,long long  y,long long  n,long long  s)
{
	tempCount=0;
	dfs(0,a,b,a1,a1,s,n);
	return tempCount;
}
int  main()
{
	long long  n,s,a,b,a1,i,k,lowLimit,highLimit,items,counter=0;
	cin>>n>>s>>a>>b;
	items=n*(n-1)/2;
	lowLimit=(s-items*a)/n;
	highLimit=(s+items*b)/n;
	
	for(a1=lowLimit;a1<=highLimit;a1++)
		for(i=0;i<=items;i++)
			if(n*a1+i*a-(items-i)*b==s)
			{
					counter+=more(a1,a,b,i,items-i,n,s);
					if(counter>module)
						counter%=module;	
			}
	cout<<counter<<endl;
	return 0;
}


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值