加载中...

【题解】整数划分


题目: 整数划分

题意:

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

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

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

输入格式

共一行,包含一个整数 n。

输出格式

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

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

数据范围

1 ≤ n ≤ 1000

输入样例:

5

输出样例:

7

解法一:视为完全背包问题

将给定的数视为背包的体积,那么题目就转变为了给我们n个体积和价值均为从1到n的物品,要求我们可以刚好将背包填满,问有多少种填法。
原本的完全背包是将集合中的所有方案比较出一个最大值,状态转移方程:f[j]=max(f[j],f[j-v[i]]+w[i]);
现在则是将完全背包集合中所有方案加在一起,状态转移方程:f[j]=f[j]+f[j-i];

#include<iostream>
#include<algorithm>

using namespace std;

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

int f[N];
int n;

int main()
{
    cin>>n;
    
    //当n==0时,只有一种选法,就是什么也不选
    f[0]=1;
    for(int i=1;i<=n;++i)
    {
        //完全背包容易被忽略的一点是j的起始值要注意,通常是当前物品的体积
        for(int j=i;j<=n;++j)
            f[j]=(f[j]+f[j-i])%M;
    }
    cout<<f[n];
    return 0;
}

解法二:其它解法

将f[i][j]的集合视为两个子集,分别是方案中含有数字的最小值是1和方案中所含数字最小值大于1两个。
状态转移方程就是两者之和:f[i][j]=f[i-1][j-1]+f[i-j][j];
关键点就在于不重不漏的分割集合

#include<iostream>
#include<algorithm>

using namespace std;

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

//f[i][j]表示所有总和是i,且刚好有j个数的方案  
int f[N][N];
int n;

int main()
{
    cin>>n;
    
    f[0][0]=1;
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n;++j)
            if(i>=j)
                f[i][j]=(f[i-1][j-1]+f[i-j][j])%M;
    }
    
    int res=0;
    for(int i=1;i<=n;++i)
        res=(res+f[n][i])%M;
    cout<<res;
    return 0;
}

文章作者: 心意
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 心意 !
评论
  目录