蓝桥杯——波动序列(动态规划+滚动数组)

题目:

在这里插入图片描述

输入输出:

在这里插入图片描述

题目分析:

首先假设第一个数为 x,那么后面的数都是在 x的基础上进行加 a或者减 b操作,将这一操作用 P={a,-b}来表示,那么最终的和一定为:
x + ( x + P ) + ( x + 2 P ) + . . . + ( x + ( n − 1 ) P ) = s x + (x+P) + (x+2P) + ... +(x+(n-1)P) = s x+(x+P)+(x+2P)+...+(x+(n1)P)=s
变换一下上述的等式为: n x + ( 1 + 2 + 3 + . . . + n − 1 ) P = s nx + (1+2+3+...+n-1)P = s nx+(1+2+3+...+n1)P=s
通过上述公式可知 P的总个数是一定的,为:n(n-1)/2 ,并且由于 x为整数,那么
x = ( s − n ( n − 1 ) / 2 ∗ P ) / n x = (s - n(n-1)/2*P) / n x=(sn(n1)/2P)/n

依据上述的说明,我们可以就其中的 a的个数进行讨论,a的个数和的范围是 [0,n(n-1)/2],可以使用动态规划来进行分析:
dp[i][j]用于表示前 i个元素组成和为 j的方案数,那么有如下的情况:

  • 当 i 大于 j 时,由于 i 的系数为 i ,所以 dp[i][j]必然是从dp[i-1][j]转换来的,也就是说不能使用 i
  • 当 i 小于等于 j 时,由于j大于 i,而i又可以 使用 或者 不使用,所以有下面的转换:
    dp[i][j] = dp[i-1][j] + dp[i-1][j-i]

按照上述的规则,i的范围应该为 1到 n-1,j为0到n(n-1)/2,显然内存会占用过多,再观察上述公式,发现当前的状态只是与前一个状态有关,所以可以使用滚动数组进行解决。

代码展示:
#include <iostream>
#include <cstring>
#include <cstdio>
#define MOD 100000007
using namespace std;

const int MAXN = 1005;
long long dp[2][MAXN*MAXN];
long long n,s,a,b;
int main()
{
    scanf("%lld%lld%lld%lld",&n,&s,&a,&b);
    dp[0][0] = 1;
    int flag = 0;
    for(int i=1;i<n;++i)
    {
        flag = 1 - flag;
        for(int j=0;j<=i*(i+1)/2;++j)
            if(j < i)
                dp[flag][j] = dp[1-flag][j];
            else
                dp[flag][j] = (dp[1-flag][j]%MOD + dp[1-flag][j-i]%MOD)%MOD;
    }
    long long ans,cnt = 0;
    for(int i=0;i<=n*(n-1)/2;++i)
    {
        ans = s - i*a + (n*(n-1)/2-i)*b;
        if(ans % n == 0)
            cnt = (cnt%MOD+dp[flag][i]%MOD)%MOD;
    }
    printf("%lld\n",cnt);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值