题目链接
https://www.acwing.com/problem/content/1216/
思路来源
https://www.acwing.com/solution/content/72795/
题目详情:
观察这个数列:
1 3 0 2 -1 1 -2 …
这个数列中后一项总是比前一项增加2或者减少3,且每一项都为整数。
栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加 a 或者减少 b 的整数数列可能有多少种呢?
输入格式
共一行,包含四个整数 n,s,a,b,含义如前面所述。
输出格式
共一行,包含一个整数,表示满足条件的方案数。
由于这个数很大,请输出方案数除以 100000007 的余数。
数据范围
1≤n≤1000
−10^9≤s≤10^9
1≤a,b≤10^6输入样例:
4 10 2 3输出样例:
2样例解释
两个满足条件的数列分别是2 4 1 3和7 4 1 -2。
分析:
- 转化问题:
设这个数的第一项为x,设d∈{+a,-b},则长度为n的序列所有的数为:
x,x+d1,x+d2,x+d3,…,x+dn−1
可以化为:
nx + (n-1)*d1 + (n-2)*d2 + ... + dn-1 = s
x = (s - ((n-1)*d1 + (n-2)*d2 + ... + dn-1)) / n
求满足这个式子 的方案数有多少,因为x一定是整数,所以 (s−((n−1)d1+(n−2)d2+(n−3)d3+…+dn−1)) % n 一定为 0 ,因此推出 s % n == (n−1)d1+(n−2)d2+(n−3)d3+…+dn−1(n−1)d1+(n−2)d2+(n−3)d3+…+dn−1的和 % n,也就是两者的模n的余数必须相同。
而s是确定的,所以我们最后就是要求 (n−1)d1+(n−2)d2+(n−3)d3+…+dn−1 这个式子的所有可能的和模n的余数是 s%n 的结果数。
- 解决问题:
在上一个步骤中,我们将问题转换成了 dp组合问题,接下来用dp的方法来解决:
我们按第 i 个数+a 或者是 -b 来划分集合,前 i 个数的和 % n的结果即 j 的值会因为我们第i步选择的不同(+a或-b)而不同,但范围始终在 0~n-1
状态表示:设 f[i][j] 为选了i个数,前 i 个 d 的和模 n 的余数为 j 的集合的数量
明确目标:最后求得是 f[n−1][s%n] 的值递推关系:第 i 次可以选择 +a 也可以选 -b
如果选 +a ,前 i 个数的和为 :
[(n−1)d1 +(n−2)d2 +…+(n−(i−1))di−1+(n−i)a]%n ≡ j%n (j=s%n,所以j%n = j)
(n−1)d1 +(n−2)d2 +…+(n−(i−1))di−1 ≡ j−(n−i)a (mod n)(n的同余关系)
因为 f[i][j ]代表的是组合数量,因为 j-(n-i)a 是已经确定的数值,所以变化的数量在前面的和里面,可以推出 f[i][j]=f[i−1][(j−a(n−i))%n]
同理,如果选-b,
f[i][j]=f[i−1][j+b(n−i)]
状态转移方程为:f[i][j]=f[i−1][(j+b(n−i))%n]+f[i−1][(j−a(n−i))%n]
注意:
图中的 i*a,它与(n - 1)* a 的结果是等价的,它相当于将 (n−1)d1+(n−2)d2+(n−3)d3+…+dn−1 变换成 1*d1+2*d2+...+(n-1)*dn ,不是等价变换,而是将各个变量的含义交换一下,因为题中+a,-b这两个选择可以任意组合,没有限制
代码:
//2022.5.25
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010,MOD = 100000007;
int n,s,a,b;
int f[N][N];
int getMod(int x,int y)
{
//在计算机中 负数模正数得到的不是我们现实生活中计算出的正余数
//如在我们平时的计算中,-2%10=8,但在计算机中结果为-2
//所以要进行转换,转换过程如下
return (x%y + y)%y;
}
int main()
{
cin>>n>>s>>a>>b;
//前 0 项的和 %n 的结果为 0 的集合数,也是一种合法情况
//可以理解成当 n == s 时,此时的答案就为 1
f[0][0] = 1;
//为什么 i 不会等于 n 呢?
//因为 第一项 x 我们并没有算进来,x在此题中我们当作已知的由系统给出
//我们计算的实际是序列中的第2项~第n-1项
for(int i=1;i<n;i++)
for(int j = 0;j <= n-1; j++)
{
f[i][j] = (f[i-1][getMod(j-(n-i)*a,n)]+
f[i-1][getMod(j+(n-i)*b,n)])%MOD;
}
cout<<f[n-1][getMod(s,n)];
return 0;
}


3417

被折叠的 条评论
为什么被折叠?



