整数划分
给你一个正整数N,请将其划分成n1+n2+n3…的形式,不考虑顺序,请求有多少种方案。
N<=1000
思路一
:将问题抽象成完全背包问题
问题的特点和背包问题很像:
- 可以取任意件物品
- 具有容量–背包性‘
状态表示:f[i][j] 表示1-i的数字组合成合为j的全部组合数
状态转移方程
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
+
f
[
i
−
1
]
[
j
−
i
]
+
f
[
i
−
1
]
[
j
−
2
∗
i
]
+
.
.
.
.
f[i][j] = f[i-1][j]+f[i-1][j-i]+f[i-1][j-2*i]+....
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-i]=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
]
f[i][j] = f[i-1][j]+f[i][j-i]
f[i][j]=f[i−1][j]+f[i][j−i]
滚动数组优化成一维
f
[
j
]
=
f
[
j
]
+
f
[
j
−
i
]
f[j]=f[j]+f[j-i]
f[j]=f[j]+f[j−i]
给出代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010,mod = 1e9+7;
int f[N];
int n;
int main()
{
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]<<endl;
return 0;
}
思路2:
状态表示:f[k][i] 表示用k个数字并且合为i的组合数
状态转移:
f
[
k
]
[
i
]
=
f
[
k
−
1
]
[
i
−
1
]
+
f
[
k
]
[
i
−
k
]
f[k][i]=f[k-1][i-1]+f[k][i-k]
f[k][i]=f[k−1][i−1]+f[k][i−k]
f
[
k
]
[
i
]
可
以
从
最
小
的
数
字
为
1
与
最
小
的
数
字
大
于
1
的
状
态
转
移
过
来
f[k][i]可以从最小的数字为1与最小的数字大于1的状态转移过来
f[k][i]可以从最小的数字为1与最小的数字大于1的状态转移过来
下面给出代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010,mod = 1e9+7;
int f[N][N];
int n,ans;
int main()
{
cin>>n;
f[0][0] = 1;
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
f[i][j] = (f[i-1][j-1]+f[i][j-i])%mod;
for(int i=1;i<=n;i++)ans = (ans + f[i][n])%mod;
cout<<ans<<endl;
return 0;
}