【问题描述】
将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5; 1,5,1; 5,1,1;
问有多少种不同的分法。
【输入格式】
两个整数:n,k。
【输出格式】
一个整数,表示方案数
【输入样例】
7 3
【输出样例】
4
【数据范围】
6 < n <= 200,2 <= k <= 6
【分析】
本题一种思路可以参考P1144整数划分
定义递归函数run(t,k,s)
→t为第几个加数,k为上一个加数,s为当前和
加数分别为a1,a2…ak
因为1,1,5; 1,5,1; 5,1,1视作同一种因此可以仿照整数划分搜索时
底层条件就是当s==n且t==k+1时cnt++
void f(int t,int p,int s)
{
if(s==n)
{
if(t==k+1)//此处t+1是因为第t次最后一个数搜索完后会进入t+1次,若在t==k时就判断,则a[t]没有录入数据
{
cnt++;return;
}
else return;
}
for(int i=p;i<=n-s;i++)
{
a[t]=i;
f(t+1,a[t],s+a[t]);
}
不过经实际检验,因为6 < n <= 200,2 <= k <= 6
当数据稍微一大就会严重超时,干瞪着眼等着程序跑,所以进行一定程度的优化:
1.对于上面的算法,实则有很多种情况都是不必要的
例如1,1,1;1,1,2…..1,1,4只有1,1,5才是符合的
所以对于第t个数,我们可以直接令其为n-s
接着还要判断 if(n-s>=p)
最后成立才cnt++
2. 这道题与p1144不同的是,只需输出方案数,而不必输出每一种方案的情况,所以不必用定义数组保存(定义的话也无所谓)
这样一来,时间大大缩小,毕竟剪掉一大半不必搜索的路径
核心代码
void f(int t,int p,int s)//t为第几个加数,p为前一个加数,s为当前和
{
if(t==k)//分析最后一个加数
{
a[t]=n-s;
if(a[t]>=a[t-1]) cnt++;
return;
}
for(int i=p;i<=n-s;i++)
{
a[t]=i;
f(t+1,a[t],s+a[t]);
}
}