数的划分(深搜剪枝+题解)

                                                           数的划分

解题思路 

       本题的意思是将一个数n化解为k份,有几种不同的方案。例如k=3的时候,a+b+c=n,求a、b、c有几种不同的取值。为了避免出现重复,搜索的时候按照从大到小搜索。依次枚举a、b、c的值进行判断。

       直接搜程序的运行速度是特别慢的,需要控制好搜索的上下界。

约数条件:

      (1)由于我们是按照从小到大搜索的,所以扩展节点的下界,不能小于前一个扩展节点的值。

           (意思是搜索下个值,一定大于等于当前值)

      (2)接下来考虑上界,看图吧

              

          将21分为6份,当搜索到a[i]的时候(也就搜索到5的时候),判断搜索上下界,显然搜索的上界是a[i-1](也就是4,            因为我们是按照递增的顺序进行搜索的)

          接下来判断下界,a[i] (a[i]=5)这个数肯定比n(n=21)小,但是用n作为下界,有大的区间完全没用,那怎么才              能比5稍微大一点点呢?那就是求取没被搜索到的数的平均数,如果将已经搜到的数加起来:

          设定m=6表示总共要分6份,n = n - a[1] + ... + a[i-1];(表示还有多大的值没被搜索出来,就是5+6的值)

          当搜索到4的时候,是搜到了第四个数,k = 4;

          后边的平均数就等于n/(m-k+1);

          后边的数取平均数,肯定是大于等于a[i]的。

          控制好上下界,这道题就解决了。

例题地址:https://loj.ac/problem/10018

AC代码:

#include<bits/stdc++.h>

using namespace std;
int n,m,sum = 0;
int a[10];

void dfs(int k)
{
    if(n == 0)
        return;
    if(k == m){
        if(n >= a[k])//最后一个数,只要保证大于等于前一个数,就可以
            sum++;
        return;
    }
    for(int i = a[k];i <= n/(m-k+1);i++){//设定搜索的上下界,剪枝的一种
        a[k+1] = i;
        n -= i;
        dfs(k+1);
        n += i;
    }
}
int main(){
    scanf("%d%d",&n,&m);
    a[1] = 1;
    dfs(1);
    printf("%d
",sum);
    return 0;
}

我走的很慢,但我从不后退。                                         

——林肯 

原文地址:https://www.cnblogs.com/codepeanut/p/12920498.html