一本通网站 1440:【例题1】数的划分 及 剪枝小结

原题  传送门

【题目描述】

将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。

例如:n=7,k=3,下面三种分法被认为是相同的。

1,1,5; 1,5,1; 5,1,1;

问有多少种不同的分法。 输出一个整数,即不同的分法。

【输入】

两个整数n,k(6<n≤200,2≤k≤6),中间用单个空格隔开。

【输出】

一个整数,即不同的分法。

【输入样例】

7 3

【输出样例】

4

【提示】

四种分法为:1,1,5;1,2,4;1,3,3;2,2,3。

本题就是求把数字n无序划分成k份的方案数。也就是求方程X1+X2+……Xk=n,1<=X1<=X2<=……Xk的解数。

搜索的方法是依次枚举X1,X2,……Xk的值,然后判断,如果这样一直直接搜索,程序的运行速度是非常慢的。但由于本题的数据规模比较小,如果控制好扩展结点的上界和下界,也是能够很快得出解的。

约束条件:

1.由于分解数不考虑顺序,因此我们设定分解数依次递增,所以扩展点时的下界应该是不小于前一个扩展点的值,即a[i-1]<=a[i];

2.假设我们将n已经分解成了a[1]+a[2]+……+a[i-1],则a[i]的最大值为i~k这k-i+1分平均划分,即设m=n-(a[1]+a[2]+……+a[i-1]),则a[i]<=m/(k-i+1),所以扩展点的上界是m/(k-i+1);

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int read()
{
    char ch=getchar();
    int a=0,x=1;
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') x=-x;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        a=(a<<3)+(a<<1)+(ch-'0');
        ch=getchar();
    }
    return a*x;
}
int n,m,a[9],s=0;
void dfs(int x)
{
    if(n==0) return ;       //若将n分解完,则直接返回 
    if(x==m)                //若此时分解数的个数到了m 
    {
        if(n>=a[x-1]) s++;
        return ;
    }
    for(int i=a[x-1];i<=n/(m-x+1);i++)  //从下界枚举到上界 
    {
        a[x]=i;
        n-=i;
        dfs(x+1);
        n+=i;      //回溯 
    }
 } 
 int main()
 {
     n=read();
     m=read();
     a[0]=1;        //从1开始分解 
     dfs(1);
     cout<<s<<endl;
     return 0;
 }

对神搜的剪枝技巧进行小结:

所谓剪枝就是通过某种判断,避免一些不必要的遍历过程,形象地说,就是减去搜索树中的某些枝条,故城剪枝。

剪枝的原则:

1.正确性;

2.准确性;

3.高效性;

深度优先搜索的优化技巧:

1.优化搜索顺序;

2.排除等效冗杂;

3.可行性剪枝;

4.最优性剪枝;

5.记忆化;

原文地址:https://www.cnblogs.com/xcg123/p/10991783.html