[蓝桥杯 2014 省 A] 波动数列

文章讨论了如何使用动态规划解决一个关于波动数列的问题,给定一个数列,求长度为n,和为s且后一项与前一项按给定差值a或b变化的整数数列的方案数,要求输出方案数除以100000007的余数。
摘要由CSDN通过智能技术生成

[蓝桥杯 2014 省 A] 波动数列

题目描述

观察这个数列:

1 , 3 , 0 , 2 , − 1 , 1 , − 2 , ⋯ 1,3,0,2,-1,1,-2, \cdots 1,3,0,2,1,1,2,

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

栋栋对这种数列很好奇,他想知道长度为 n n n 和为 s s s 而且后一项总是比前一项增加 a a a 或者减少 b b b 的整数数列可能有多少种呢?

输入格式

输入的第一行包含四个整数 n , s , a , b n,s,a,b n,s,a,b,含义如前面说述。

输出格式

输出一行,包含一个整数,表示满足条件的方案数。由于这个数很大,请输出方案数除以 100000007 100000007 100000007 的余数。

样例 #1

样例输入 #1

4 10 2 3

样例输出 #1

2

提示

【样例说明】

这两个数列分别是 2 4 1 3 和 7 4 1 -2。

【数据规模与约定】

对于 10 % 10\% 10% 的数据, 1 ≤ n ≤ 5 1 \le n \le 5 1n5 0 ≤ s ≤ 5 0 \le s \le 5 0s5 1 ≤ a , b ≤ 5 1 \le a,b \le 5 1a,b5

对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 30 1 \le n \le 30 1n30 0 ≤ s ≤ 30 0 \le s \le 30 0s30 1 ≤ a , b ≤ 30 1 \le a,b \le 30 1a,b30

对于 50 % 50\% 50% 的数据, 1 ≤ n ≤ 50 1 \le n \le 50 1n50 0 ≤ s ≤ 50 0 \le s \le 50 0s50 1 ≤ a , b ≤ 50 1 \le a,b \le 50 1a,b50

对于 70 % 70\% 70% 的数据, 1 ≤ n ≤ 100 1 \le n \le 100 1n100 0 ≤ s ≤ 500 0 \le s \le 500 0s500 1 ≤ a , b ≤ 50 1 \le a,b \le 50 1a,b50

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1000 1 \le n \le 1000 1n1000 − 1 0 9 ≤ s ≤ 1 0 9 -10^9 \le s \le 10^9 109s109 1 ≤ a , b ≤ 1 0 6 1 \le a,b \le 10^6 1a,b106

时限 1 秒, 256M。蓝桥杯 2014 年第五届省赛

Java代码

import java.util.*;
class Main
{
    public final static int MOD=100000007;
    public static void main(String[] args)
    {
        Scanner sc = new Scanner(System.in);
        int n=sc.nextInt();
        int s=sc.nextInt();
        int a=sc.nextInt();
        int b=sc.nextInt();
        
        int[][] dp = new int[n+1][n]; //第i次选择a或b,且当前总和除以n的余数是j的方案数
        dp[0][0] = 1;
        for(int i=1;i<=n-1;i++) //第一次不需要考虑选择a或b,所以此处为n-1
        {
            for(int j=0;j<n;j++)
            {
                dp[i][j]=(dp[i-1][getMod(j-i*a,n)]+dp[i-1][getMod(j+i*b,n)])%MOD;
            }
        }
        
        System.out.println(dp[n-1][getMod(s,n)]);
    }
    public static int getMod(int a,int b)       //求a除以b的正余数
    {
        return (a%b+b)%b;
    }
}

题目简单分析

d p [ i ] [ j ] dp[i][j] dp[i][j] 为上一个选 a a a 的方案数加上选 b b b 的方案数,此题关键公式如下

x = s − [ ( n − 1 ) d 1 + ( n − 2 ) d 2 + ⋯ + d n − 1 ] n x={s-[(n-1)d_1+(n-2)d_2+\cdots+d_{n-1}]\over n} x=ns[(n1)d1+(n2)d2++dn1]


其中, x x x 为第一个选的数,一定为整数且可以由某选择方案求得, s s s 为原序列和, d d d + a +a +a − b -b b,要求原序列方案数即求 d 1 d_1 d1 d n − 1 d_{n-1} dn1 的选择方案数。不难看出, s s s ( n − 1 ) d 1 + ( n − 2 ) d 2 + ⋯ + d n − 1 (n-1)d_1+(n-2)d_2+\cdots+d_{n-1} (n1)d1+(n2)d2++dn1 n n n 同余,由此可得 d p dp dp 第二个状态为对 n n n 取模的余数是 j j j 。本题解代码中,可以将 19 19 19 行中 i ∗ a i*a ia 等价换成 ( n − i ) ∗ a (n-i)*a (ni)a 更好理解,表示 ( n − 1 ) d 1 + ( n − 2 ) d 2 + ⋯ + d n − 1 (n-1)d_1+(n-2)d_2+\cdots+d_{n-1} (n1)d1+(n2)d2++dn1 中的某项。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值