整数划分
整数划分问题
给两个整数 \(n\) 和\(k\)。
将 n 划分成 k 个正整数之和的划分数
n=m1+m2+...+mi; (其中mi为正整数,并且1 <= mi <= n),则{m1,m2,...,mi}为n的一个划分。
如果{m1,m2,...,mi}中的最大值不超过m,即max(m1,m2,...,mi)<=m,则称它属于n的一个m划分。这里我们记n的m划分的个数为f(n,m);
例如当n=4时,他有5个划分,{4},{3,1},{2,2},{2,1,1},{1,1,1,1};
注意4=1+3 和 4=3+1被认为是同一个划分。
该问题是求出n的所有划分个数,即\(f(n, n)\)。下面我们先考虑求\(f(n,m)\)的方法;
递归
(1)当\(n=1\)时,不论\(m\)的值为多少(\(m > 0\) ),只有一种划分即 { 1 };
(2) 当 \(m = 1\) 时,不论\(n\)的值为多少,只有一种划分即 \(n\) 个 1,{ 1, 1, 1, ..., 1 };
(3) 当 \(n = m\) 时,根据划分中是否包含 \(n\),可以分为两种情况:
(a). 划分中包含\(n\)的情况,只有一个即 { n };
(b). 划分中不包含\(n\)的情况,这时划分中最大的数字也一定比 \(n\) 小,即 \(n\) 的所有 ( \(n - 1\) ) 划分。
因此 \(f(n, n) = 1 + f(n, n-1)\);
(4) 当 \(n < m\) 时,由于划分中不可能出现负数,因此就相当于\(f(n, n)\);
(5) 但 \(n > m\) 时,根据划分中是否包含最大值 \(m\),可以分为两种情况:
(a). 划分中包含 \(m\)的情况,即 { m, { x1, x2, ..., xi } }, 其中 { x1, x2, ..., xi } 的和为 \(n - m\),可能再次出现 \(m\),因此是(\(n - m\))的 \(m\) 划分,因此这种划分
个数为 \(f(n-m, m)\);
(b). 划分中不包含 \(m\) 的情况,则划分中所有值都比 $m $小,即 \(n\) 的 (\(m - 1\) ) 划分,个数为 \(f(n, m - 1)\);
因此 \(f(n, m) = f(n - m, m) + f(n, m - 1)\);
综合以上情况,我们可以看出,上面的结论具有递归定义特征,其中(1)和(2)属于递归边界,(3)和(4)属于特殊情况,将会转换为情况(5)。而情况(5)为通用情况,其本质主要是通过减小\(m\)以达到递归边界,从而解决问题。其递推表达式如下:
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) {
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];
}
将 n 划分成最大数不超过 k 的划分数
跟1的情况是一样的,最后输出\(dp[n][k]\)即可
将 n 划分成若干奇正整数之和的划分数
这个呢,我们首先需要调整边界状态:当\(m=1\)时,\(f(n,m)=1\);当\(n=1\)而\(m>1\)时,\(f(n,m)=0\)
其次,我们需要调整状态转换公式:
\(f(n-m,m)+f(n,m-1); (n>m)\) 应该更改为:\(f(n-m,m)+f(n,m-2); (n>m)\)
当\(n=m\)时,考虑是否取\(m\)值,分为两种情况:
取\(m\)值,那么就只有一个\(n\)
不取\(m\)值,那么转化为\(f(n,m-2)\)
所以$f(n,m)=1+f(n,m-2) $
memset(dp,0,sizeof(dp));
for(int i=0;i<=n;i++){
dp[i][1]=1;
if(i&1)//判断i是否是奇数,这是一个位运算
dp[0][i]=1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(j&1){
if(j<=i){
dp[i][j]=dp[i-j][j]+dp[i][j-1];
}
else{
dp[i][j]=dp[i][i];
}
}
else{
dp[i][j]=dp[i][j-1];
}
}
} //
将 n 划分成若干不同整数之和的划分数
只需要考虑当\(n>m\)时,考虑是否取\(m\)
取\(m\)值,即{m, {x1,x2,...xi}}, 其中{x1,x2,... xi} 的和为\(n-m\),可能再次出现\(m\),因此是(\(n-m\))的\(m\)划分,因此这种划分个数为\(f(n-m, m-1)\); 因为m不能再取了
不取\(m\)值, 划分中不包含\(m\)的情况,则划分中所有值都比\(m\)小,即\(n\)的(\(m-1\))划分,个数为\(f(n,m-1)\);
所以\(n>m\)的情况,\(f(n,m)=f(n-m,m)+f(n,m-1)\)
终止条件 :
当\(n=1\)时 \(m>=1\) 肯定是1
但是当\(n=1 ,m<1\)或者 \(n>1 m=1\)时显然是0
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) {
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];
}
将n划分为k个数的划分数(一个划分中正好有k个数)
划分中是否包含1,包含的话就是\(dp[i-1][k-1]\)(把1拿出来,然后将剩下的分为k-1份),不包含的话就是\(dp[i-k][k]\)(把1分给k份,每一份都有一个1之后将剩余的分成k份,这样就可以保证每一份都不为1)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) {
if(j==1) dp[i][j]=1;
else dp[i][j]=dp[i-1][j-1]+dp[i-j][j];
}