递归→P1145-数的划分

来源:互联网 发布:易建联卧推体测数据 编辑:程序博客网 时间:2024/06/01 21:57

【问题描述】

  将整数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]);     }}