整数划分
一个正整数n可以表示成若干个正整数之和,形如:n=n1+n2+…+nk,其中n1≥n2≥…≥nk。
我们将这样的一种表示称为正整数n的一种划分。
现在给定一个正整数n,请你求出n共有多少种不同的划分方法。
输入格式
共一行,包含一个整数n。
输出格式
共一行,包含一个整数,表示总划分数量。
由于答案可能很大,输出结果请对1e9+7取模。
数据范围
1≤n≤1000
输入样例
5
输出样例
7
题目分析
方法一
因为输入一个数n,求n共有多少种不同的划分方法。首先因为是正整数的组合,因此我们可知,选择数的数量最多为n个1,因此选择数的数量不可能超过n
因此我们何以将其当做完全背包问题来看待,将n视为背包容量,其余数字本身的数值即为数值所占容量。
因此我们设 i 与 j 两数
i表示选择的次数
j表示数值的大小
以下为状态转移方程的推导
f[i][j]= f[i-1][j]+f[i-1][j-i]+f[i-1][j-i* 2]+……+f[i-1][j-i*n]
f[i][j-i]= f[i-1][j-i]+f[i-1][j-i* 2]+f[i-1][j-i* 3]+……+f[i-1][j-i*n]
f[i][j]=f[i-1][j]+f[i][j-i]
因此代码如下
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010,M=1e9+7;
int dp[N],n;
int main()
{
cin>>n;
dp[0]=1;
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)dp[j]=(dp[j]+dp[j-i])%M;
cout<<dp[n];
return 0;
}
方法二
所有总和为i,并且恰好表示成j个数的和的方案
可以将f[i][j]的方案数分为两种情况
1:方案中存在最小值为1的情况,这种请情况下我们去除1
则可得f[i-1][j-1].
2:方案中的最小值大于1,这时方案中的每个数均减去1
则此时i=i-j,同时由于每个数都大于1,因此方案数量不会变化
因此可得 f[i-j][j]
由于f[i][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]为该题的状态转移方程。
由于i为总和,j为方案中数的个数
因此答案为(假设输入的数为n)
ans=f[n][1]+f[n][2]+……+f[n][n];
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010,M=1e9+7;
int dp[N][N],n,ans;
int main()
{
cin>>n;
dp[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)dp[i][j]=(dp[i-1][j-1]+dp[i-j][j])%M;
for(int i=1;i<=n;i++)ans=(ans+dp[n][i])%M;
cout<<ans;
return 0;
}