整数划分
一个正整数 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 ;
}