整数划分的三种解法
题目描述
样例:
5=
1 1 1 1 1
2 1 1 1
2 2 1
3 1 1
3 2
4 1
5
共7种
解法一:转化为完全背包求方案数问题
背包体积为:n
物品体积为:{1,2,3…n}
求恰好装满背包需要的方案数,每种物品可以装无数次,是一个完全背包问题
代码如下:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
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];
return 0;
}
解法二:不同的状态转移
状态表示:
f[i][j]表示为总和为i,由j个数组成的方案数
状态转移:
f[i][j],考虑j个数可以分为两种情况:包含1、不包含(大于)1
包含1,表示j个元素最小值为1,此时的方案数为f[i-1][j-1](去掉这个1的方案数)
不包含(大于)1,表示j个元素最小值大于1,此时的方案数为f[i-j][j](每个元素都-1)
f[i][j]=f[i-1][j-1]+f[i-j][j]
代码如下:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
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;
return 0;
}
解法二:分治
考虑分治成子问题:
f[i][j]表示总和为i,分解出的元素中最大的数<=j的方案数
(1)显然i<j时,可分的最大数为i到达不了j,所以
f[i][j]=f[i][i]
(2)i=j
f[i][j]=f[i][j-1]+1
加上的的这一次为n本身这一次
(3)i>j时
f[i][j]=f[i][j-1]+f[i-j][j]
f[i][j-1]表示可分的数最大值为j-1的方案数,再加上最大值为j的方案即可(注意:至少有一个j,所以表示为f[i-j][j])
代码如下:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1010,mod=1e9+7;
int f[N][N];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i<j) f[i][j]=f[i][i];
else if(i==j) f[i][j]=(f[i][j-1]+1)%mod;
else f[i][j]=(f[i][j-1]+f[i-j][j])%mod;
cout<<f[n][n];
return 0;
}