计数类DP
定义
计数类DP主要是通过动态规划的方法来计算满足特定条件的方案数、组合数等数量相关的问题。
运用情况
- 需要计算不同排列、组合或情况的数量。
 - 问题具有明显的阶段性,且每个阶段的选择会对后续阶段产生影响。
 - 可以通过逐步构建较小规模问题的解来推导出大规模问题的解。
 
注意事项
- 状态定义要准确合理,确保能够涵盖所有需要计数的情况。
 - 边界条件的处理要小心,避免出现错误。
 - 注意状态转移的正确性和完整性,不能遗漏某些可能的情况。
 - 避免重复计算,确保 DP 过程的高效性。
 
解题思路
- 确定状态:仔细分析问题,找到合适的状态表示,通常状态包含问题规模、已有的某些特征等。
 - 分析状态转移:找出不同状态之间的联系,即如何从一个状态推导出下一个状态的方案数。
 - 初始化:对边界状态或初始状态进行正确的赋值。
 - 递推求解:按照状态转移方程逐步计算出更大规模问题的解。
 - 得到最终结果:根据问题要求,从最终状态中获取需要的计数结果。
 
例如,计算从一个起点到一个终点有多少种不同走法的问题,就可以用计数类 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;
} 
代码思路
-  
初始化:首先设置
f[0] = 1,表示选择0个元素的组合只有1种情况(什么都不选)。 -  
双重循环:
- 外层循环变量
i从1到n,代表当前考虑的是将多少个元素作为一个整体(从1开始是因为至少选1个元素才有组合变化)。 - 内层循环变量
j从i到n,表示在考虑将i个元素作为整体时,可以放置的位置(或理解为累计到目前为止的选择总数)。 - 在内循环中,更新
f[j]的值为f[j] + f[j - i],并取模mod。这里的意思是,对于已经有j-i个元素的组合,我们再添加一个由i个相同元素组成的组合,形成一个新的组合。因此,到达某一总数j的组合数是之前所有小于j的组合数累加的结果,体现了组合数学中的加法原理。 
 - 外层循环变量
 -  
输出结果:经过上述计算后,
f[n]即为从1到n的所有整数中选取任意个数的组合总数的和。 


















