动态规划一日一题-10.3-整数划分


整数划分


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

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

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

输入格式

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

输出格式

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

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

数据范围

1≤n≤1000

输入样例:

5

输出样例:

7

该题是典型的整数划分类问题,也可以将其理解成完全背包问题。所以该题就可以转换为现在有n种物品,第i种背包的容量是i,数量为无穷,请问能装满体积为n的背包的方案数是多少?

同完全背包问题,我们先想朴素算法。用f[i] [j] 表示用前i个物品来装满体积为j的背包的方案数。

状态表示:f[i] [j] 表示用前i个物品装满j的背包的方案数,最终的结果就可以标示为f[n] [ n] ;

状态属性:无

状态转移:f[i] [j] = f[i-1] [ j] +f[i-1] [j-i] +f[i-1] [j-2*i] + …+

​ f[i] [j-i] = f[i-1] [j-i] + f[i-1] [j- 2*i] + … +

故有数学推导可得:

f[i] [j] = f[i-1] [j] + f[i] [j-i]

base case :比较玄学,基于现在的水平,我觉得就是把二维数组涉及下标为0的可以提前知道的给初始化一下,比如这一题:

f[i] [0] 是可以提前知道的,同时也是转移的起始点。

//朴素做法

#include 

using namespace std ;

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

int f[N][N] ;
int main()
{
    int n ; 
    cin >> n  ;
    
    //base case
    
    for(int i = 1 ; 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 

using namespace std ;

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

int f[N] ; 

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

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