概念
编辑求划分个数
编辑分析
递归代码:
#include<bits/stdc++.h>
using namespace std;
int equation(int n,int m)
{
if(n==1||m==1)
return (1);
else if(n<m)
return equation(n,n);
else if(n==m)
return 1+equation(n,n-1);
else
return equation(n-m,m)+equation(n,m-1);
}
int main()
{
int equation(int n,int m);
int n,m;
printf("Please input 'n'(0<n<100):");
scanf("%d",&n);
printf("Please input 'm'(0<m<=n):");
scanf("%d",&m);
printf("quantity:%d\n",equation(n,m));
}
以上内容来自百度百科
题目网址:整数划分问题
整数划分问题
HIT - 1402整数划分是一个经典的问题。希望这道题会对你的组合数学的解题能力有所帮助。
Input每组输入是两个整数n和k。(1 <= n <= 50, 1 <= k <= n)
Output对于每组输入,请输出六行。
第一行: 将n划分成若干正整数之和的划分数。
第二行: 将n划分成k个正整数之和的划分数。
第三行: 将n划分成最大数不超过k的划分数。
第四行: 将n划分成若干奇正整数之和的划分数。
第五行: 将n划分成若干不同整数之和的划分数。
第六行: 打印一个空行。
5 2Sample Output
7 2 3 3 3
Hint:
- 将5划分成若干正整数之和的划分为: 5, 4+1, 3+2, 3+1+1, 2+2+1, 2+1+1+1, 1+1+1+1+1
- 将5划分成2个正整数之和的划分为: 3+2, 4+1
- 将5划分成最大数不超过2的划分为: 1+1+1+1+1, 1+1+1+2, 1+2+2
- 将5划分成若干奇正整数之和的划分为: 5, 1+1+3, 1+1+1+1+1
- 将5划分成若干不同整数之和的划分为: 5, 1+4, 2+3
思路:
1.将n划分成若干正整数之和的划分数(一定要完全理解!!!)
dp[n][k]表示n这个数划分为最大值不超过k的划分数
初始化条件:n==1||k==1时,dp[n][k]=1,因为最大k=1的时候只有一种分法就是n个数字都为1,或者n=1了,也只有一种分法就是一个1
还有dp[0][1~n]=1,因为当n=k时,且划分中含有k的情况此时dp[n-k][k]应该为1而不是0,所以我们将dp[0][1~n]赋值为1
《1》当n>=k时,dp[n][k]由两种状态可以得到,即n的划分中有k和没k
①dp[n-k][k],意为n的划分中含有k
②dp[n][k-1],意为n的划分中没有k
此时dp[n][k]=dp[n-k][k]+dp[n][k-1]
《2》当n<k时,因为不存在负数所以dp[n][k]=dp[n][n];
2.将n划分成k个正整数之和的划分数(n个苹果分在k个盘子里面,盘子不允许为空,问有多少种分法)
dp[n][k]表示n这个数分为k个数字的划分数-------n个苹果分在k个盘子里
初始化条件:当k=1的时候,dp[n][k]=1,只有一个盘子了只有一种情况
《1》n>=k时,dp[n][k]由两种状态得到,即n的划分中有1和没1
①dp[n-1][k-1],意为n的划分中含有1--------有一个盘子只放一个苹果,以后就不考虑这个盘子了
②dp[n-k][k],意为n的划分中没有1--------每个盘子都放一个苹果
《2》n<k时,dp[n][]k]=0--------n小于k,n个苹果放k个盘子,盘子还不允许为空,所以这种情况为0
3.将n划分为最大数不超过k的划分数(跟第一问基本一模一样,POJ-1664跟这个问题一样)
4.将n划分成若干奇正整数之和的划分数
dp[n][k]表示n这个数划分为最大值不超过k的划分数
初始化条件:dp[0~n][1]的值为1,任何数划分为不超过1的数都只有一种情况,dp[0][奇数]=1(原因同第一个问题)
《1》k为奇数
①n>=k时,dp[n][k]由两种状态来,一dp[n-k][k],意为n的划分中含有k,二dp[n][k-1],意为n的划分中没有k
②n<k时,dp[n][k]=dp[n][n],因为不存在负数;
《2》k为偶数,dp[n][k]=dp[n][k-1]
5.将n划分为若干不同正整数之和的划分数
dp[n][k]表示n这个数划分为最大值不超过k且不同的划分数
初始化条件:n==1时,dp[n][k]=1,因为最n=1了,也只有一种分法就是一个1
还有dp[0][1~n]=1,因为当n=k时,且划分中含有k的情况此时dp[n-k][k]应该为1而不是0,所以我们将dp[0][1~n]赋值为1
《1》当n>=k时,dp[n][k]由两种状态可以得到,即n的划分中有k和没k
①dp[n-k][k-1],意为n的划分中含有k,则再划分k就不能再用了
②dp[n][k-1],意为n的划分中没有k
此时dp[n][k]=dp[n-k][k]+dp[n][k-1]
《2》当n<k时,因为不存在负数所以dp[n][k]=dp[n][n];
代码如下://#include <bits/stdc++.h>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=55;
int n,k,dp[maxn][maxn];
int a()//将n划分成若干正整数之和的划分数。
{
memset(dp,0,sizeof(dp)); dp[0][0]=1;
for(int i=0;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i>=j) dp[i][j]=dp[i-j][j]+dp[i][j-1];
else dp[i][j]=dp[i][i];//i=0的时候这里会把dp[0][1~n]初始化为1
//这样后面在递推的时候会直接把dp[1][1~n]和dp[1~n][1]的情况赋值为1
}
}
return dp[n][n];
}
int b()//将n划分成k个正整数之和的划分数。
{
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(j==1) dp[i][j]=1;//j=1的时候这里会把dp[1~n][1]初始化为1
else if(i>=j) dp[i][j]=dp[i-j][j]+dp[i-1][j-1];
}
}
return dp[n][k];
}
int c()//将n划分为若干最大值不超过k的正整数之和 除了返回值和a()不一样其他都一样
{
memset(dp,0,sizeof(dp)); dp[0][0]=1;
for(int i=0;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i>=j) dp[i][j]=dp[i-j][j]+dp[i][j-1];
else dp[i][j]=dp[i][i];//i=0的时候这里会把dp[0][1~n]初始化为1
//这样在递推的时候会直接把dp[1][1~n]和dp[1~n][1]的情况赋值为1
}
}
return dp[n][k];
}
int d()// 将n划分成若干奇正整数之和的划分数。
{
memset(dp,0,sizeof(dp));
for(int i=0;i<=n;i++)//初始化dp数组
{
dp[i][1]=1;
if(i&1) dp[0][i]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(j&1)
{
if(i>=j) 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];
}
}
return dp[n][n];
}
int e()//将n划分成若干不同整数之和的划分数。
{
memset(dp,0,sizeof(dp));dp[0][0]=1;
for(int i=0;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i>=j) dp[i][j]=dp[i-j][j-1]+dp[i][j-1];
else dp[i][j]=dp[i][i];//i=0的时候这里会把dp[0][1~n]初始化为1
//这样在递推的时候会直接把dp[1][1~n]的情况赋值为1
}
}
return dp[n][n];
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
printf("%d\n%d\n%d\n%d\n%d\n\n",a(),b(),c(),d(),e());
}
return 0;
}
母函数法:
问题:将n划分为若干个正整数的划分数
我们可以构建一个函数:
g(x)=(x0+x1+x2+x3+...+xn)(x0+x2+x4+x6+...)(x0+x3+x6+x9+...)...(x0+xn)
第i个括号$(1+x^i+x^{2i}+x^{3i} · ·)$选择的元素代表了数字i在我们最终的划分中出现的次数
我们不要管X是什么因为我们用不到他,我们只用这个函数的指数和系数;这个多项式展开后X^n次方的系数就是n的划分数。
对于这样一个式子X^3是如何得到的呢,从第一个括号中挑X^3,其他括号挑X^0,或者从第一个括号挑X^1,第二个括号挑X^2,其他括号挑X^0,再或者从第三个括号挑X^3,
所以X^3的系数为三,正好是X^3的划分数。
我们可以看一下4的划分为:①1+1+1+1,②1+1+2,③3+1,④2+2,⑤4共五种情况,(也正好是这个构造多项式的X^4的系数,大家可以手推算一下有助于理解)
①从第一个括号中挑X^4代表四个1,其他括号全挑X^0
②从第一个括号中挑X^2代表俩个1,第二个括号中挑X^2代表一个2,其他括号全挑X^0
③从第一个括号中挑X^1代表一个1,第三个括号中挑X^3代表一个三
④从第二个括号中挑X^4代表两个2,其他括号全挑X^0
⑤从第四个括号中挑X^4代表一个4,其他括号全挑X^0
在这个多项式中X^4也就是这五种方法得到的。
例题OpenJ_Bailian-4117
网址:简单的整数划分问题
题意:将n划分为若干个正整数的划分数
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=55;
int a[maxn],b[maxn],c[maxn];
void Poly()///多项式乘法
{
memset(c,0,sizeof(c));
for(int i=0;i<maxn;i++)
{
for(int j=0;j<maxn-i;j++)
{
c[i+j]+=(a[i]*b[j]);
}
}
}
int main()
{
for(int i=0;i<maxn;i++) a[i]=1;// 第一个括号g(x,1)=x^0+x^1+x^2+x^3...
for(int i=2;i<maxn;i++)
{
memset(b,0,sizeof(b));
for(int j=0;j<maxn;j+=i) b[j]=1;// 第i个括号g(x,j)=x^(0*j)+x^(1*j)+x^(2*j)+x^(3*j)...
Poly();
memcpy(a,c,sizeof(c));//相乘结果从c复制到a
}
int n;
while(cin>>n)
{
cout<<a[n]<<endl;
}
return 0;
}
例题UESTC-624
网址:整数划分
题意:将n划分为若干个不相同的正整数
生成函数:
g(x)=(x0+x1)(x0+x2)(x0+x3)...(x0+xn)
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1005;
const int MOD=19901014;
int a[maxn],b[maxn],c[maxn];
void Poly(int k)///多项式乘法
{
memset(c,0,sizeof(c));
for(int i=0;i<maxn;i++)
{
for(int j=0;j<maxn-i;j+=k)
{
c[i+j]+=(a[i]*b[j])%MOD;
}
}
}
int main()
{
memset(a,0,sizeof(a));a[0]=a[1]=1;//第一个括号 g(x,1)=x^0+x^1
for(int i=2;i<maxn;i++)
{
memset(b,0,sizeof(b));
b[0]=b[i]=1;// 第二个括号 g(x,j)=x^(0*j)+x^(1*j)
Poly(i);
memcpy(a,c,sizeof(c));//相乘结果从c复制到a
}
int T,n;scanf("%d",&T);
while(T--)
{
scanf("%d",&n);printf("%d\n",c[n]);
}
return 0;
}
这里再附一份我看不懂的代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1005;
const int MOD=19901014;
int c1[maxn],c2[maxn];
int main()
{
c1[0]=1;c1[1]=1;
for(int i=2;i<maxn;i++)
{
for(int j=0;j<maxn;j++)
{
c2[j]+=c1[j]%MOD;
if(j+i<maxn) c2[i+j]+=c1[j]%MOD;
}
for(int j=0;j<maxn;j++)
{
c1[j]=c2[j];
c2[j]=0;
}
}
int T,n;scanf("%d",&T);
while(T--)
{
scanf("%d",&n);printf("%d\n",c1[n]);
}
return 0;
}
问题:将n划分为若干个奇正整数之和的划分数
生成函数:
g(x)=(x0+x1+x2+x3+...+xn)(x0+x3+x6+x9+...)(x0+x5+x10+x15+...)...
代码读者自己练习把。
该问题还有些变种,如要求整数连续等等;
本人学识浅薄,如有错误,望大家不吝赐教;