今天碰到了一道整数划分的题,想起以前也做过好几道这种题,于是就把这类问题总结下。
类型一:求将整数N分解成K个正整数的方案数(K个正整数不需要不同)。
dp[i][j]表示将i分解成j个正整数的方案数,考虑j个正整数中是否有1,如果有1,那么dp[i][j] = dp[i-1][j-1],相当于拿出一个1,剩余就是将i-1分成j-1个正整数的方案数。如果没有1,那么dp[i][j]=dp[i-j][j],相当于将i-j这个数分成j个正整数,然后将这个j个正整数都加上1就是将i这个数分成了j份。
综上,dp[i][j] = dp[i-1][j-1]+dp[i-j][j]。
类型二:求将整数N分解成N个不相同的正整数的个数的方案数。
dp[i][j]表示将i分解成j个不相同的正整数的方案数,依然考虑这j个数字中是否有数字1,如果没有数字1,那么显然dp[i][j] = dp[i-j][j],原因和第一种类型相同。
如果有数字1,那么dp[i][j]肯定不等于dp[i-1][j-1],因为将i-1分解成j-1个正整数可能也会出现1,再加上一开始的1,这样就出现了重复数字,不符合题意。
考虑将i-j分解成j-1份,再将这个j-1个数每个数字加上1,这样这j-1个数肯定就不存在数字1,然后再添加一个数字1,这样就相当于将i-j+j-1+1 = i分解成了j-1+1=j份,而且还不存在重复元素,这样dp[i][j] = dp[i-j][j-1].
综上dp[i][j] = dp[i-j][j]+dp[i-j][j-1]。
类型三(类型二的扩展):求将整数N分解成N个不相同的奇正整数的个数的方案数。
这个和类型二完全一样,如果理解了类型二那么类型三也很显然。
dp[i][j]表示将i分解成j个不相同的奇正整数的方案数,依然考虑这j个数字中是否有数字1,如果没有数字1,这样j个数最小值就是3,那么将这j个数字每个数字都减去2,这种情况就是一种可行解,dp[i][j] = dp[i-2*j][j].
如果存在数字1,考虑将i-1-2*(j-1)分解成j-1份,再将这个j-1个数每个数字加上2,这样这j-1个数肯定就不存在数字1,然后再添加一个数字1,这样就相当于将i-1-2*(j-1)+2*(j-1)+1分解成了j-1+1=j份,而且还不存在重复元素,这样dp[i][j] = dp[i-1-2*(j-1)][j-1].
综上dp[i][j] = dp[i-2* j][j]+dp[i-1-2*(j-1)][j-1].
类型四:将N分解成不大于K的若干个正整数的方案数。
dp[i][j]表示将i分解成不大于j的若干个正整数的方案数。考虑这j个数是否存在数字j,若不存在,则dp[i][j] = dp[i][j-1],若存在,那么去掉这个最大值,在将剩余i-j这个数字分解成不大于j的若干个正整数之和即可,即dp[i][j] = dp[i-j][j].
综上,dp[i][j] = dp[i][j-1]+dp[i-j][j].
以下是具体题目
OpenJ_Bailian - 4119 :
将正整数n 表示成一系列正整数之和,n=n1+n2+…+nk, 其中n1>=n2>=…>=nk>=1 ,k>=1 。
正整数n 的这种表示称为正整数n 的划分。
Input
标准的输入包含若干组测试数据。每组测试数据是一行输入数据,包括两个整数N 和 K。
(0 < N <= 50, 0 < K <= N)
Output
对于每组测试数据,输出以下三行数据:
第一行: N划分成K个正整数之和的划分数目
第二行: N划分成若干个不同正整数之和的划分数目
第三行: N划分成若干个奇正整数之和的划分数目
#include <bits/stdc++.h>
using namespace std;
int a[55][55];
int b[55][55];
int c[55][55];//不同的奇数
int main()
{
a[0][0] = 1<