[Acwing蓝桥杯DP] 1214. 波动数列

题目: 1214. 波动数列 - AcWing题库

题目大意:长度为 n 和为 s 而且后一项总是比前一项增加 a或者减少 b 的整数数列可能有多少种

数据范围:    1<n<1000;

                   -10^9<s<10^9;

                    1<=a,b<=1e6;   

这个题用普通的思维去考虑很难,确实想不到

y总就那么一列 一设变量 一观察 就出来了 真的yyds!

自己确实想一百年也想不到

现将y总的思路总结一下,方便以后复习吧。

设第一个数为x,则第二个数为x+d1,第三个数为x+d1+d2…。这里的d1或d2表示a或者−b

所以这个数列为:  x x+d1 x+d1+d2 .... x+d1+d2...+dn-1

又有已知条件 : (x)  +  (x+d1) + (x+d1+d2) + ... + (x+d1+d2+dn-1) = s

重点来了: 由于x是初始的值,x可以取任意的整数 范围是相当大的 如果枚举 是不可能的

所以 转化 转化啊  就很神奇 

把 x 提到方程的一边  

为:

 

 

 来观察 观察:

发现不用 关注x 只需要 s 除以 n的余数 等于 除以n的余数即可

 

就是模n相同!amazing!

到这里就转化成了组合问题

下面就可以用闫氏dp分析法

 

集合:f [ i ][ j ] 表示:只考虑前 i 个数 且余数是 j 的所有集合的数量

状态计算: 只考虑第i个数 只能取 a 或 -b 。

分析: 第i个数 选择 a 那么 [ ( n - 1 ) * d1 + ( n - 2 ) * d2 + ( n - 3 ) * d3 + ... dn-1  ]% n = j ;  

最后一个第 n项  变量为dn-1 我们已经假设其 为 a 则对应的 系数是 ( n - i );

所以 状态转移方程为:f [ i ] [ j ] = f [ i -1 ][ j - ( n - i) * a] 和f [ i ] [ j ] = f [ i -1 ][ j + ( n - i) * b] 

这样基本就解决了

还有初始化问题 : 本题初始化非常简单,由于是求方案数

则 初始化 f [ 0 ][ 0 ]=1 不选的时候,余数为0 ,其他的余数均不合法。

代码如下:

 

复制代码
#include <bits/stdc++.h>

using namespace std;

const int N=1010,MOD=100000007;

int n,s,a,b;
int f[N][N];

int get(int a,int b)//返回 a % b 的正余数
{
    return (a % b + b ) % b;
}

int main()
{
    cin>>n>>s>>a>>b;
    
    f[0][0]=1;
    
    for(int i=1;i<n;i++)
    {
        for(int j=0;j<n;j++)//枚举余数 余数只能从0~1
        {
            f[i][j]=(f[i-1][get(j-a*(n-i),n)]+f[i-1][get(j+b*(n-i),n)])%MOD;
        }
    }
    
    cout<<f[n-1][get(s,n)]<<endl;
    
    return 0;
}
复制代码

end!!!

 

posted @   秦末  阅读(72)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
1 博文导航目录
点击右上角即可分享
微信分享提示