1:复杂的整数划分问题
-
总时间限制:
- 200ms 内存限制:
- 65536kB
-
描述
-
将正整数n 表示成一系列正整数之和,n=n1+n2+…+nk, 其中n1>=n2>=…>=nk>=1 ,k>=1 。
正整数n 的这种表示称为正整数n 的划分。
输入
-
标准的输入包含若干组测试数据。每组测试数据是一行输入数据,包括两个整数N 和 K。
(0 < N <= 50, 0 < K <= N)
输出
-
对于每组测试数据,输出以下三行数据:
第一行: N划分成K个正整数之和的划分数目
第二行: N划分成若干个不同正整数之和的划分数目
第三行: N划分成若干个奇正整数之和的划分数目
样例输入
-
5 2
样例输出
-
2 3 3
提示
-
第一行: 4+1, 3+2,
第二行: 5,4+1,3+2
第三行: 5,1+1+3, 1+1+1+1+1+1
-
当对划分整数没有限制,dp[i][j]表示整数i,不超过
-
dp[i][j] = 1 + dp[i][j-1] when i=j;
-
dp[i][j]=dp[i-j][j]+dp[i][j-1] when j<i;
-
dp[i][1] = 1
-
dp[1][i] = 1
-
当j>i dp[i][j]=dp[i][i];
-
当要求划分整数必须不同时,同样的有
-
dp[i][1]=0,dp[1][i]=1
-
dp[i][j] = 1 + dp[i][j-1] when i=j;
-
dp[i][j]=dp[i-j][j-1]+dp[i][j-1] when j<i;
-
要求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,可以使用“截边法”将j个划分分别截去一个1,把问题转化为i-j的j-1个划分数,为dp[i-j][j-1]; 若划分中不包含1,使用“截边法”将j个划分数的最下面一个数截去,将为题转化为求i-j的j个划分数,为dp[i-j][j]。所以i>j时dp[i][j]=dp[i-j][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]。
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划分为若干奇数的划分数,为问题4的答案。
-
#include<cstdio> #include<iostream> #include<cstring> using namespace std; #define MAXN 50 #define MAXM #define INF 0x3f3f3f3f typedef long long int LL; int N,K; LL dp[MAXN+10][MAXN+10]; void work1()//选K个 { memset(dp,0,sizeof(dp)); for(int i=1;i<=N;++i)//将i分成1个数只有一种方案 dp[i][1]=1; for(int i=1;i<=N;++i) for(int j=2;j<=i;++j)//将每个数统一减1,或去掉当前数中的1 dp[i][j]=dp[i-j][j]+dp[i-1][j-1]; printf("%d\n",dp[N][K]);//把N分成K个数 } void work2()//任意不同 { memset(dp,0,sizeof(dp)); dp[0][0]=1; for(int i=0;i<=N;i++) { for(int j=1;j<=N;j++) { //当前有数是j和降低上限 if(j<=i)dp[i][j]=dp[i-j][j-1]+dp[i][j-1]; else dp[i][j]=dp[i][i];//上限应为i } } printf("%lld\n",dp[N][N]);//划分N,上限为N } void work3()//任意奇数(基本同work1) { memset(dp,0,sizeof(dp)); for(int i=0;i<=N;++i) { dp[i][1]=1; if(i&1)dp[0][i]=1;//预处理第0层 } for(int i=1;i<=N;i++) { for(int j=1;j<=N;j++) { if(j&1)//同work1 { 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];//当前非奇数 } } printf("%lld\n",dp[N][N]); } int main() { while(~scanf("%d%d",&N,&K)) { work1(); work2(); work3(); } }
-