1214. 波动数列 - AcWing题库
// 卡壳,漏了题目关键流程要取模,由于是例题就没有按流程debug导致
// 错误:i的范围应该是1到n-1,因为数组的第一项是任意的,且已经分离出去了,这里只有第二项开始往后的数
// 卡壳,j的范围没有确定好,模n余数应该是0到n-1
// 错误取模的数错误,这里数组保存的是题目结果,应该模 100000007,n是对于下标才要模的数
dp分析时,在分析状态表示时,要分析每一维的取值范围
最后检查范围时,不仅要检查数据范围,还要检查取模情况
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; // // c++中的负数取模会得到负数,但数学中的负数取模是应该得到正数的,要写上一个取模函数把负数取模补正 int get_mod(int a,int b){ return (a % b + b) % b; } // 卡壳,漏了题目关键流程要取模,由于是例题就没有按流程debug导致 const int N = 1e3 + 10,MOD = 100000007; int f[N][N]; int main(){ int n,s,a,b; cin >> n >> s >> a >> b; f[0][0] = 1; for(int i = 1;i <= n;i++){ // 错误:i的范围应该是1到n-1,因为数组的第一项是任意的,且已经分离出去了,这里只有第二项开始往后的数 for(int j = 0;j <= n;j++){ // 卡壳,j的范围没有确定好,模n余数应该是0到n-1 f[i][j] = (f[i-1][get_mod(j-(n-i)*a,n)] + f[i-1][get_mod(j + (n-i)*b,n)]) % MOD; // 错误取模的数错误,这里数组保存的是题目结果,应该模 100000007,n是对于下标才要模的数 } } cout << f[n-1][get_mod(s,n)]; // 错误,同i范围没有确定的连带错误 return 0; }
思路
// 1≤n≤1000
// ,
// −109≤s≤109
// ,
// 1≤a,b≤106
// 范围在暗示用枚举类的方法,想dfs,dp
// 观察这个数列:
// 1 3 0 2 -1 1 -2 …
// 这个数列中后一项总是比前一项增加2或者减少3,且每一项都为整数。
// 栋栋对这种数列很好奇,他想知道长度为 n
// 和为 s
// 而且后一项总是比前一项增加 a
// 或者减少 b
// 的整数数列可能有多少种呢?
// 表达式
// x + x + d1 + x + d1 + d2 + x + d1 + d2 + d3 + ... = s;
// s = n*x + (n-1)d1 + (n-2)d2 + (n-3)*d3 + ...;
// x无限制,用有限制的量来表示
// s - ((n-1)d1 + (n-2)d2 + (n-3)*d3 + ...) / n = x;
// 因为x是整数,所以
// s - ((n-1)d1 + (n-2)d2 + (n-3)*d3 + ...)一定是n的倍数
// 根据?同余定理,
// s 同余 ((n-1)d1 + (n-2)d2 + (n-3)*d3 + ...) (mod n)
// 看((n-1)d1 + (n-2)d2 + (n-3)*d3 + ...),要枚举每一个d选a还是b,可以开始dp分析,
// 两个选择的dp,排列问题dp,选择问题dp,(母题为背包问题)
// // 状态表示 f[i][j] 二维数组表示考虑前i项,j表示余数的情况数
// // 属性 :情况数
// 情况数属性要边界初始化
// // // 状态表示写的时候考虑好取值范围
// // i范围是1到n-2,因为这里通过变换把第一项给去掉了,所以少一项
// // j的范围,0到n
// // 状态计算 集合划分,最后一项不同的,f[i][j],第i项选a f[i-1][j-(n-1)*a]
// // 第i项选b f[i-1][j+(n-1)*b]
// // 第i项选a的话,余数加上(n-1)*a = j,所以i-1的余数是(j-(n-1)*a)%n
// // 第i项选b的话,余数减去(n-1)*b = j;所以i-1的余数是(j+(n-1)*b;)%n
// // 由于这个数很大,请输出方案数除以 100000007
// // 的余数。
// // f[i][j] = (f[i-1][(j-(n-1)*a)%n] + f[i-1][(j+(n-1)*b)%n])%mod;
// // c++中的负数取模会得到负数,但数学中的负数取模是应该得到正数的,要写上一个取模函数把负数取模补正
// 检查范围
// 1≤n≤1000
// ,
// −109≤s≤109
// ,
// 1≤a,b≤106
// 取模是1e8的,没有*10操作,不可能爆int