波动数列(组合问题求方案数)
波动数列

思路O(n^2)
设第一个数为x,则第二个数为\(x+d1\),第三个数为\(x+d1+d2 …\)。这里的d1,d2表示a或者−b,所以这个数列为:
\(x,x + d_{1}, x + d_{1} + d_{2}, x + d_{1} + d_{2} + d_{3},... ...,x + d_{1} + d_{2} + ... + d_{n - 1}\)
和为s转换为:
\(n * x + (n - 1) * d_{1} + (n - 2) * d_{2} + ... ... + d_{n - 1} = s\)
转换为:
\(\frac{s - [(n - 1) * d_{1} + (n - 2) * d_{2} + ... + d_{n - 1}\ ]}{n} = x\)
即\(s\)和\((n - 1) * d_{1} + (n - 2) * d_{}2 + ... + d_{n - 1}\)模n的余数相同转换成组合问题
状态表示:f[i, j]表示要选i个a或者-b且余数为j的所有集合的方案数.
第 i 选a :\(f[i, j] = f[i - 1, j - (n - i) * a]\)
第 i 选b:\(f[i, j] = f[i - 1, j + (n - i) * b]\)
样例输入:
4 10 2 3
样例输出:
2
代码:
#include<iostream>
using namespace std;
const int N = 1010, mod = 100000007;
int n, s, a, b;
int f[N][N]; //选前i个数中 余数为j的方案数
int get_mod(int a, int b) // 求a除以b的正余数 --> core
{
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 ++ )
{
f[i][j] = (f[i - 1][get_mod(j - (n - i) * a, n)] + f[i - 1][get_mod(j + (n - i) * b, n)]) % mod;
}
}
cout << f[n - 1][get_mod(s, n)] << endl;
return 0;
}

浙公网安备 33010602011771号