整数划分——计数类DP(c++)

AcWing 900. 整数划分
一个正整数n可以表示成若干个正整数之和,形如:n=n1+n2+…+nk,其中n1≥n2≥…≥nk,k≥1。

我们将这样的一种表示称为正整数n的一种划分。

现在给定一个正整数n,请你求出n共有多少种不同的划分方法。

输入格式
共一行,包含一个整数n。

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

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

数据范围
1≤n≤1000
输入样例:

5

输出样例:

7

思路:把1,2,3, … n分别看做n个物体的体积,这n个物体均无使用次数限制,问恰好能装满总体积为n的背包的总方案数(完全背包问题变形)

原题目题意就是从 1 ~ n 中选选任意个数 组成 n 也就是说从 1 ~ i 中选任意个数加起来等于j; 
 所以这个题就为完全背包问题的变式! 
 f[i][j] 的状态表示: i   1 ~ i 中选 总体积恰好为 j
 f[i][j] 的性质 : 数量;
 f[i][j] 的计算: 第i个物品选k个;
f[i][j] = f[i-1][j] + f[i-1][j-i] + f[i-1][j - i*2] + ....f[i-1][j-i*s];
f[i][j - i] =         f[i-1][j-i] + f[i-1][j - i*2] + ....f[i-1][j-i*s];
合并一下 f[i][j] = f[i-1][j] + f[i][j-i];
优化成一维 f[j] = f[j] + f[j-i];

作者:粥粥。

完全背包解法

状态表示:
f[i][j]表示只从1~i中选,且总和等于j的方案数

状态转移方程:

f[i][j] = f[i - 1][j] + f[i][j - i];
#include <iostream>
#include <algorithm>

using namespace std;

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

int n;
int f[N];

int main()
{
    
    
    cin >> n;

    f[0] = 1;// 一个都不选的情况下 而且体积 为0 所以 数量只有一个;
    // 比如f[1] , 表示一个都不选 但是体积为 1 的时候 所以一定为 0;
    for (int i = 1; i <= n; i ++ )
        for (int j = i; j <= n; j ++ )// 完全背包用不到i - 1 层 从小到大枚举体积就OK 
            f[j] = (f[j] + f[j - i]) % mod;

    cout << f[n] << endl;

    return 0;
}

其他算法

在这里插入图片描述

状态表示:
f[i][j]表示总和为i,总个数为j的方案数

状态转移方程:
f[i][j] = f[i - 1][j - 1] + f[i - j][j];

#include <iostream>
#include <algorithm>

using namespace std;

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

int n;
int f[N][N];表示组成总和为i的j个数的所有方案

int main()
{
    
    
    cin >> n;

    f[1][1] = 1;
    for (int i = 2; i <= n; i ++ )
        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;
}

猜你喜欢

转载自blog.csdn.net/Annabel_CM/article/details/111702817
今日推荐