下面的描述大部分借鉴于(https://blog.csdn.net/Lin_disguiser/article/details/50574818), 感谢, 但是其中有部分错误, 我会在下面的描述中纠正过来…
总的解决方法时截边法, 也就是去讨论有1无1的情况和截去他们的情况….. 记住了.
一 求将n划分为若干正整数之和的划分数
1. 若划分的多个整数可以相同
设dp[i][j]为将i划分为不大于j的划分数
(1) 当i < j 时,i不能划分为大于i的数,所以dp[i][j]=dp[i][i];
(2) 当i>j 时,可以根据划分中是否含有j分为两种情况。若划分中含有j,划分方案数为dp[i-j][j];若划分数中不含j,相当于将i划分为不大于j-1的划分数,为dp[i][j-1]。所以当i>j时dp[i][j]=dp[i-j][j]+dp[i][j-1];
(3) 当i=j 时,若划分中含有j只有一种情况,若划分中不含j相当于将i划分为不大于j-1的划分数。此时dp[i][j]=1+dp[i][j-1]
2. 若划分的正整数必须不同
设dp[i][j]为将i划分为不超过j的不同整数的划分数
(1) 当i< j时,i不能划分为大于i的数,所以dp[i][j]=dp[i][i];
(2) 当i>j时,可以根据划分中是否含有j分为两种情况。若划分中含有j,则其余的划分中最大只能是j-1,方案数为dp[i-j][j-1];若划分中不含j,相当于将i划分为不大于j-1的划分数,为dp[i][j-1]。所以当i>j时dp[i][j]=dp[i-j][j-1]+dp[i][j-1];(与上面的的转移方程唯一不同的地方, dp[i-j][j-1])
(3) 当i=j时,若划分中含有j只有一种情况,若划分中不含j相当于将i划分为不大于j-1的划分数。此时dp[i][j]=1+dp[i][j-1]
dp[n][n]表示将n划分为不同整数的划分数
二 将n划分为k个整数的划分数
设dp[i][j]为将i恰好划分为j个整数的划分数 (解答描述已纠正)
(1) i< j为不可能出现的情况,dp[i][j]=0;
(2) 若i=j,有一种情况:i可以划分为i个1之和,dp[i][j]=1;
(3) 若i>j,可以根据划分数中是否含有1分为两类:若划分数中含有1,可以使用“截边法”将1截去,把问题转化求i-1的j-1个划分数,为dp[i-1][j-1]; 若划分中不包含1,使用“截边法”将j个划分数每个都截去一个1将问题题转化为求i-j的j个划分数,为dp[i-j][j]。所以i>j时 dp[i][j] = dp[i-1][j-1]+dp[i-j][j].
三 将n划分为若干正奇数之和的划分数
设f[i][j]为将i恰好划分为j个奇数之和的划分数,g[i][j]为将i恰好划分为j个偶数之和的划分数
使用截边法,将g[i][j]的j个划分都去掉1,可以得到f[i-j][j],所以
g[i][j] = f[i-j][j] (因为总的考虑1的情况, 所以注重讨论奇数的情况)
f[i][j]中有包含1的划分方案和不包含1的划分方案。对于包含1的划分方案,可以将1的划分除去,转化为“将i-1划分为j-1个奇数之和的划分数”,即f[i-1][j-1];对于不包含1的划分方案,可以使用截边法对j个划分每一个都去掉一个1,转化为“将i-j划分为j个偶数之和的划分数”,即g[i-j][j]。
所以f[i][j]=f[i-1][j-1]+g[i-j][j]。
f[n][0]+f[n][1]+……+f[n][n]为将n划分为若干奇数的划分数的方案数.
四 将n划分为不多于k个正整数的划分数
设dp[i][j] 代表 i 划分为不多于j个正整数的划分数
所以:
(1) 当 i == 1 || j == 1时, dp[i][j] = 1; 前面为只有1这个情况, 后面为划分为自身的情况.
(2) 当 i == j 时, dp[i][j] = dp[i][j-1] + 1; 表示将i划分为不多于j-1个的方案数, + 全是1组成i的情况.
(3) 当 i < j 时, 没有多余方案, dp[i][j] = dp[i][i];
(4) 当 i > j 时, 分两种齐情况, 一是如果这不多于j个正整数中有1,那么可以将这个1合并到任意一个数字中, 也就是求i 划分为不多于j-1个正整数的方案, 而是如果 这不多于j个正整数中没有1, 那么可以利用截边法将每一个数字截去1, 那么就是求截去1的方案数也就是dp[i-j][j]; 所以转移方程为:
dp[i][j] = dp[i][j-1] + dp[i-j][j];
下面就是上面这些所有的问题解决模板了: 需要哪个用哪个. 唯一坑点就是可能数据量较大 , 需要long long.
ll dp[maxn][maxn];
ll f[maxn][maxn], g[maxn][maxn];
int n, k;
void div1() {
// dp[i][j] 代表将i划分为不大于j的划分数(允许重复数字)
for (int i = 1 ; i <= n ; i ++) {
for (int j = 1 ; j <= k ; j ++) {
if (j == 1) dp[i][j] = 1;
else if (i == j) dp[i][j] = dp[i][j-1] + 1;
else if (i < j) dp[i][j] = dp[i][i];
else dp[i][j] = dp[i][j-1] + dp[i-j][j];
}
}
}
void div2() {
// dp[i][j] 代表将i划分为不大于j的划分数(没有重复数字)
for (int i = 1 ; i <= n ; i ++) {
for (int j = 1 ; j <= k ; j ++) {
if (j == 1) {
if (i == 1) dp[i][j] = 1;
else dp[i][j] = 0; // 初始化不同即可, 转移方程依旧相同.
}
else if (i == j) dp[i][j] = dp[i][j-1] + 1;
else if (i < j) dp[i][j] = dp[i][i];
else dp[i][j] = dp[i][j-1] + dp[i-j][j-1];
}
}
}
void div3() {
// dp[i][j]为将i恰好划分为j个整数的划分数
for (int i = 1 ; i <= n ; i ++) {
for (int j = 1 ; j <= k ; j ++) {
if (i == j) dp[i][j] = 1;
else if (i < j) dp[i][j] = 0;
else dp[i][j] = dp[i-1][j-1] + dp[i-j][j];
}
}
}
void div4() {
//设f[i][j]为将i恰好划分为j个奇数之和的划分数
// g[i][j]为将i恰好划分为j个偶数之和的划分数。
f[0][0] = g[0][0] = 1;
for (int i = 1 ; i <= n ; i ++) {
for (int j = 1 ; j <= k ; j ++) {
if (i < j) f[i][j] = g[i][j] = 0;
else {
f[i][j] = f[i-1][j-1] + g[i-j][j];
g[i][j] = f[i-j][j];
}
}
}
}
void div5() {
// dp[i][j] 代表i划分为不多于j个正整数的划分数
for (int i = 1 ; i <= n ; i ++) {
for (int j = 1 ; j <= k ; j ++) {
if (i == 1 || j == 1) dp[i][j] = 1;
else if (i == j) dp[i][j] = dp[i][j-1] + 1;
else if (i < j) dp[i][j] = dp[i][i];
else dp[i][j] = dp[i][j-1] + dp[i-j][j];
}
}
}
代码都是可以通过上面的问题的….. 所以下次遇到了直接取就行……