计数类DP——AcWing 900. 整数划分

计数类DP

定义

计数类DP主要是通过动态规划的方法来计算满足特定条件的方案数、组合数等数量相关的问题。

运用情况

  1. 需要计算不同排列、组合或情况的数量。
  2. 问题具有明显的阶段性,且每个阶段的选择会对后续阶段产生影响。
  3. 可以通过逐步构建较小规模问题的解来推导出大规模问题的解。

注意事项

  1. 状态定义要准确合理,确保能够涵盖所有需要计数的情况。
  2. 边界条件的处理要小心,避免出现错误。
  3. 注意状态转移的正确性和完整性,不能遗漏某些可能的情况。
  4. 避免重复计算,确保 DP 过程的高效性。

解题思路

  1. 确定状态:仔细分析问题,找到合适的状态表示,通常状态包含问题规模、已有的某些特征等。
  2. 分析状态转移:找出不同状态之间的联系,即如何从一个状态推导出下一个状态的方案数。
  3. 初始化:对边界状态或初始状态进行正确的赋值。
  4. 递推求解:按照状态转移方程逐步计算出更大规模问题的解。
  5. 得到最终结果:根据问题要求,从最终状态中获取需要的计数结果。

例如,计算从一个起点到一个终点有多少种不同走法的问题,就可以用计数类 DP 来解决,状态可以是当前位置,转移就是根据不同的移动规则来更新方案数。通过合理定义状态和转移方程,就可以准确地计算出总的方案数。

AcWing 900. 整数划分

题目描述

900. 整数划分 - AcWing题库

运行代码

#include <iostream>
#include <cstring>
using namespace std;
const int MOD = 1e9 + 7;
int dp[1001][1001];
int divide(int n, int m) {
    if (n == 0) return 1;
    if (m == 0) return 0;
    if (dp[n][m]!= -1) return dp[n][m];
    int res = 0;
    for (int i = 0; i <= min(n, m); i++) {
        res = (res + divide(n - i, i)) % MOD;
    }
    dp[n][m] = res;
    return res;
}
int main() {
    int n;
    cin >> n;
    memset(dp, -1, sizeof(dp));
    cout << divide(n, n) << endl;
    return 0;
}

代码思路

  • dp[n][m] 中的 n 表示要划分的整数,m 表示当前划分中允许的最大整数。
  • divide 函数通过递归的方式计算划分方法的数量。如果 n 为 0,则表示一种划分成功,返回 1;如果 m 为 0 则返回 0。然后通过循环从 0 到 min(n, m) 逐步尝试将当前的数拆分成当前最大数和剩余部分,对剩余部分继续递归调用,将所有结果累加并取模更新状态,最后将计算结果存储在 dp 数组中。在 main 函数中输入 n 后,通过调用 divide(n, n) 并输出结果。

改进思路

  • 可以添加一些注释提高代码的可读性。
  • 可以考虑使用更高效的数据结构或算法来优化性能,虽然在这个规模下可能不太明显。
  • 可以对代码结构进行一些整理和优化,使逻辑更加清晰。

其它代码

#include <iostream>
using namespace std;
const int N = 1010, mod = 1e9 + 7;
int f[N];
int main()
{
    int n;
    cin >> n;
    f[0] = 1;
    for(int i = 1; i <= n; i ++ )
        for(int j = i; j <= n; j ++ )
            f[j] = (f[j] + f[j - i]) % mod;
    cout << f[n] << endl;
    return 0;
}

代码思路

  1. 初始化:首先设置f[0] = 1,表示选择0个元素的组合只有1种情况(什么都不选)。

  2. 双重循环

    • 外层循环变量i从1到n,代表当前考虑的是将多少个元素作为一个整体(从1开始是因为至少选1个元素才有组合变化)。
    • 内层循环变量ji到n,表示在考虑将i个元素作为整体时,可以放置的位置(或理解为累计到目前为止的选择总数)。
    • 在内循环中,更新f[j]的值为f[j] + f[j - i],并取模mod。这里的意思是,对于已经有j-i个元素的组合,我们再添加一个由i个相同元素组成的组合,形成一个新的组合。因此,到达某一总数j的组合数是之前所有小于j的组合数累加的结果,体现了组合数学中的加法原理。
  3. 输出结果:经过上述计算后,f[n]即为从1到n的所有整数中选取任意个数的组合总数的和。

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

筱姌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值