算法学习笔记(46)——计数类DP

计数类 DP

题目链接:AcWing 900. 整数划分

问题描述

一个正整数 n 可以表示成若干个正整数之和,形如:n=n1+n2++nk,其中 n1n2nk,k1
我们将这样的一种表示称为正整数 n 的一种划分。
现在给定一个正整数 n,请你求出 n 共有多少种不同的划分方法。

输入格式

共一行,包含一个整数 n

输出格式

共一行,包含一个整数,表示总划分数量。

由于答案可能很大,输出结果请对 109+7 取模。

数据范围

1n1000

输入样例

5

输出样例

7

算法1(完全背包)

可将本题完全背包问题,容量是 N 的背包,共有N 件物品,每件物品的体积分别是 1N,每种物品有无限个,求恰好装满背包的方案数。

状态表示

F[i,j] 表示从第 1i 件物品中选,恰好装满 j 体积的方案数。

阶段划分

最后一件物品的个数。

  • 0 件第 i 个物品,F[i1,j]
  • 1 件第 i 个物品,F[i1,ji]
  • 2 件第 i 个物品,F[i1,j2i]
  • ......(以此类推)
  • k 件第 i 个物品,F[i1,jki],kij,(k+1)i>j

转移方程

F[i,j]=F[i1,j]+F[i1,ji]+F[i1,j2i]++F[i1,jki]F[i,ji]=F[i1][ji]+F[i1,j2i]++F[i1,jki]

利用完全背包的优化思路,将第二个方程代入第一个方程,得到:

F[i,j]=F[i1,j]+F[i,ji]

边界

容量为0时,前 i 个物品全不选也是一种方案

F[i,0]=1,其余为0

目标

F[N][N]

#include <iostream>

using namespace std;

const int N = 1010, mod = 1e9 + 7;

int n;
int f[N][N];

int main()
{
    cin >> n;
    
    // 容量为0时,前 i 个物品全不选也是一种方案
    for (int i = 0; i <= n; i ++ ) f[i][0] = 1;
    
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ ) {
            f[i][j] = f[i - 1][j] % mod;
            if (j >= i) f[i][j] = (f[i - 1][j] + f[i][j - i]) % mod;
        }
            
    cout << f[n][n] << endl;
    
    return 0;
}

维数优化:

#include <iostream>

using namespace std;

const int N = 1010, mod = 1e9 + 7;

int n;
int f[N];

int main()
{
    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;
}

算法2

img

方案中最小值是 1 时的方案数,与去掉一个 1 的方案数 F[i1,j1] 相同。
方案中最小值大于 1 时的方案数,与对每一个数减 1 (共有 j 个数,所以总和减 j)的方案数 F[ij,j] 相同。

转移方程

F[i,j]=F[i1,j1]+F[ij,j]

边界

F[0,0]=1

目标

F[n,1]+F[n,2]++F[n,n]

#include <iostream>

using namespace std;

const int N = 1010, mod = 1e9 + 7;

int n;
int f[N][N];

int main()
{
    cin >> n;
    
    f[0][0] = 1;
    for (int i = 1; i <= n; i ++ )
        // 总和是i得数最多表示成i个数的和(均为1)
        for (int j = 1; j <= i; j ++ )
            f[i][j] = (f[i - 1][j - 1] + f[i - j][j]) % mod;
         
    int res = 0;
    for (int i = 1; i <= n; i ++ ) res = (res + f[n][i]) % mod;
    cout << res << endl;
    
    return 0;
}
posted @   S!no  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示