递归——整数规划问题
问题描述:
将正整数n表示成一系列正整数之和:n=n1+n2+…+nk,其中n1≥n2≥…≥nk≥1,k≥1。正整数n的这种表示称为正整数n的划分。
例如:
6
5+1
4+2, 4+1+1
3+3,3+2+1,3+1+1+1
2+2+2,2+2+1+1,2+1+1+1+1
1+1+1+1+1+1
问题1:
求正整数n的不同划分个数,将最大数n1不大m的划分记住做q(n,m),叫做n的m划分。
输入:n m
输出:n的m划分的总个数。
思路:
首先要找出递归的公式来,首先分析几种简单的情况,n == 1 || m == 1可以直接得出结果为1;而当n<m时,可以直接求出q(n,n);当n=m时,因为对于n本身只有一种情况,即n,所有可以直接用1+q(n,n-1)来求。最后当n>m时,可以用q(n,m-1)+q(n-m,m),其中q(n-m,m表示的时当m固定后,求剩下可能的情况。参考下图:
代码:
//求整数划分的个数
#include <stdio.h>
int Divid(int n, int m)
{
if (n == 1 || m == 1)
return 1; //必须是或
if (n < m)
return Divid(n,n);
if (n == m)
return 1 + Divid(n,n-1);
return Divid(n,m-1) + Divid(n-m,m);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
printf("%d\n",Divid(n,m));
return 0;
}
问题2:
输出整数n的所有可能的划分
思路:
递归搜索所有可能的情况,同时为了记录下每一步的情况,那么就要用到一个数组mark来存储每一步的数,然后递归同时要传递递归的深度k。还有个问题就是递归下一个数的时候,因为是递减的排列的。所以我们还必须记录下上一个的数,然后下一个数必须小于或者等于上一个数。最后递归函数还有有个参数记录当前的长度,来判断是否能够组成我们想要的长度,不能的话就回溯,继续往下一个数去尝试。OK!
代码:
//整数划分问题
#include <stdio.h>
int mark[10];
int n;
void Divid(int now,int k,int prio)
{
//now记录当前长度,k记录深度,prio记录前一个的值。
int i;
if(now > n)
return; //不合适,返回。
if(now == n)
{
for(i = 0; i < k-1; i++)
printf("%d+",mark[i]);
printf("%d\n",mark[i]);
}
else
{
for(i = prio; i > 0; i--)
{
if(i <= prio) //必须必前一个要小
{
mark[k]=i;
now+=i;
Divid(now,k+1,i);
now-=i;
}
}
}
}
int main()
{
scanf("%d",&n);
Divid(0,0,n-1);
return 0;
}