(排列,选择类dp)(数论同余定理,同余运算)(以背包为母题)1214. 波动数列

9 篇文章 0 订阅
6 篇文章 0 订阅

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
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值