【ACWing】900. 整数划分

题目地址:

https://www.acwing.com/problem/content/902/

给定一个正整数 n n n,问其表示为若干正整数之和的方案数,即问 n = n 1 + n 2 + . . . + n k , n 1 ≥ n 2 ≥ . . . ≥ n k , k ≥ 1 n=n_1+n_2+...+n_k,n_1\ge n_2\ge ...\ge n_k,k\ge 1 n=n1+n2+...+nk,n1n2...nk,k1的取法数(即与顺序无关)。

数据范围:
1 ≤ n ≤ 1000 1\le n\le 1000 1n1000

可以看成是完全背包问题,相当于问有 n n n个不同物品,每个物品体积是 1 , 2 , . . . , n 1,2,...,n 1,2,...,n,问恰好塞满容量是 n n n的背包有多少个方案。思路是动态规划,设 f [ i ] [ j ] f[i][j] f[i][j]是只选 1 ∼ i 1\sim i 1i这几个物品的情况下,塞满容量是 j j j的背包的方案数,那么可以按照 i i i这个物品选不选来分类,如果不选,那方案数就是 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j],否则,方案数就是 f [ i ] [ j − i ] f[i][j-i] f[i][ji],所以 f [ i ] [ j ] = max ⁡ { f [ i − 1 ] [ j ] , f [ i ] [ j − i ] } f[i][j]=\max\{f[i-1][j],f[i][j-i]\} f[i][j]=max{ f[i1][j],f[i][ji]}可以做空间优化,只存一行的值,并且每行从左到右遍历( f [ i ] [ j ] f[i][j] f[i][j]要用到当前行左边的值)。初始值是 f [ 0 ] [ 0 ] = 1 f[0][0]=1 f[0][0]=1 f [ 0 ] [ . > 0 ] = 0 f[0][.>0]=0 f[0][.>0]=0,最后答案是 f [ n ] [ n ] f[n][n] f[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;
}

时间复杂度 O ( n 2 ) O(n^2) O(n2),空间 O ( n ) O(n) O(n)

还可以采取另一种思路,设 f [ i ] [ j ] f[i][j] f[i][j] i i i能表示为 j j j 1 ∼ n 1\sim n 1n的正整数之和的方案数,那么可以按照划分的最小整数来分类。如果最小整数是 1 1 1,那么去掉这个 1 1 1,就相当于是将 i − 1 i-1 i1划分为 j − 1 j-1 j1个正整数的方案数,是 f [ i − 1 ] [ j − 1 ] f[i-1][j-1] f[i1][j1];如果最小整数大于 1 1 1,则将划分每个数都减去 1 1 1,就得到了 i − j i-j ij划分为 j j j个数的方案数,是 f [ i − j ] [ j ] f[i-j][j] f[ij][j]。所以 f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + f [ i − j ] [ j ] f[i][j]=f[i-1][j-1]+f[i-j][j] f[i][j]=f[i1][j1]+f[ij][j]最后答案是 ∑ j = 1 n f [ n ] [ j ] \sum_{j=1}^n f[n][j] j=1nf[n][j]。初始条件 f [ 0 ] [ 0 ] = 1 f[0][0]=1 f[0][0]=1 f [ 0 ] [ . > 0 ] = 0 f[0][.>0]=0 f[0][.>0]=0。代码如下:

#include <iostream>
using namespace std;

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

int main() {
    
    
    int n;
    cin >> n;

    f[0][0] = 1;
    for (int i = 1; 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;
}

时空复杂度 O ( n 2 ) O(n^2) O(n2)

猜你喜欢

转载自blog.csdn.net/qq_46105170/article/details/114053060
今日推荐