第五届蓝桥杯C/C++本科A组初赛波动数列解题报告

来源:互联网 发布:软件质量分析师 编辑:程序博客网 时间:2024/04/30 06:20

原题可化为nx+(n-1)p(1)+(n-2)p(2)+…+p(n)=s,其中n为数列长度,x为初值,p(i)={a,-b}。本题的目标是给出不同的p序列,使得等式成立并且x为整数。自然而然可以想到枚举的方法,给出不同的序列,令t=s-Σi*p(n-i),若t%n==0则是一种可取的方案。直接枚举肯定会超时,所以需要进一步考虑。注意到,a和b的总数为n(n-1)/2个(所有p前系数的和),所以我们只需要枚举a的个数,将t修改为t=s-ca-(n(n-1)/2-c)b,c为枚举数,0<=c<=n(n-1)/2。

当然满足条件的c的个数并不是我们想要的,因为给定一个c,存在多种组合方式。但是可以发现,每个c都是由1~n-1中若干元素组成的。于是问题转化为求容量为c的01背包的方案数。对于本题,可以写为:(f[i][j]为前i个物体构成j体积的方案数,第i个物体的体积为i)

f[i][j]=f[i-1][j],  i>j

         f[i-1][j]+f[i-1][j-i],  j>=i

注意到递推式只和前一状态有关,故可以使用滚动数组;根据定义,可以预先算出每一个f[n][i],避免重复计算;前i个货物最多只能达到i*(i+1)/2的体积,所以大于这个数值的部分没有必要计算。具体的请看代码,其实就这么几行。

#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#define MAXN 1100#define MOD 100000007using namespace std;int F[2][MAXN*MAXN];int e = 0;long long n,s,a,b;int cnt = 0;void calc(int elem){    int i,j;    memset(F,0,sizeof(F));    F[e][0]=1;    for(i=1;i<n;i++)    {        e=1-e;        for(j=0;j<=i*(i+1)/2;j++)        {            if(i>j)                F[e][j]=F[1-e][j];            else                F[e][j]=(F[1-e][j]+F[1-e][j-i])%MOD;        }    }}int main(){    scanf("%I64d%I64d%I64d%I64d",&n,&s,&a,&b);    long long i,t;    calc(n*(n-1)/2);    for(i=0; i<=n*(n-1)/2; i++)    {        t = s - i*a + (n*(n-1)/2-i)*b;        if(t%n==0)           cnt = (cnt+F[e][i])%MOD;    }    printf("%d",cnt);    return 0;}


0 0
原创粉丝点击