深搜剪枝技巧

                        深搜的剪枝技巧

不会搜索的这里→搜索与回溯

一、什么是剪枝

    剪枝,顾名思义,就是通过某种判断,避免一些不必要的搜索。在编写程序时,一般都要考虑剪枝

  显而易见,优化的核心在于设计剪枝判断方法决定哪些应该舍去,那些不该舍。

二、剪枝原则

  1. 正确性:剪枝的目的是优化程序,但如果将正解减去,那优化就失去意义了。
  2. 准确性:在正确的基础上,尽量剪去更多的不能通向正解的枝条。
  3. 高效性:也就是不能比不剪枝时间复杂度要高。

三、几种剪枝技巧

数的划分

题目描述

将整数n分成k份,且每份不能为空,问有多少种不同的分法。当  n=7,k=3时,下面三种分法被认为是相同的:1 1 5 ; 1 5 1 ; 5 1 1 

输入格式

一行两个数 n,k 。

输出格式

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

样例输入
7 3
样例输出
4

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

6<=n<=200   k<=6

  首先面临的问题是如何将相同的分法删掉,其次就是时间复杂度。

  可以考虑,每一次都搜索大于等于上一次的数,不仅可以避免相同的分法,还可以剪枝,

在一个剪枝就是,如果选第t个之前的值为m,那么第k个数必然不能大于(n-m)/(k-t+1)     //因为还没选第t个数,故考虑+1;

不然根据我们原则,后面选的数必须大于前面选的数,肯定会超过n。

  最后一个小优化,如果选完k-1个数了,那第k个数是什么就已经定了,不需要再去枚举,只需看它是否符合原则。

分析过后,代码应不难想出

#include<iostream>
using namespace std;
int n,k,ans;
int a[1000];
void search(int t)
{
    if(n==0)
    return ;
    if(t==k)
    {
    if(n=a[t-1])
    ans++;
    return ;    
    }
    for(int i=a[t-1];i<=n/(k-t+1);i++)  //剪枝,若i比n/(t-k+1)大,则后面即使取能取到的最小值,也会大于n; 
    {
        n-=i;
        a[t]=i;
        search(t+1);
        n+=i;
    }
}
int main()
{
    cin>>n>>k;
    a[0]=1;
    search(1);
    cout<<ans;
}

[SHOI2002]滑雪     
普及/提高-

Michael 喜欢滑雪。这并不奇怪,因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael 想知道在一个区域中最长的滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子:

1   2   3   4   5
16  17  18  19  6
15  24  25  20  7
14  23  22  21  8
13  12  11  10  9

一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度会减小。在上面的例子中,一条可行的滑坡为 2424-1717-1616-11(从 2424 开始,在 11 结束)。当然 2525-2424-2323-\ldots…-33-22-11 更长。事实上,这是最长的一条。

输入格式

输入的第一行为表示区域的二维数组的行数 RR 和列数 CC。下面是 RR 行,每行有 CC 个数,代表高度(两个数字之间用 11 个空格间隔)。

输出格式

输出区域中最长滑坡的长度。

输入输出样例

输入 #1
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
输出 #1
25
这题就是记忆化搜索
我们将样例放大
1   2   3   4   5
16  17  18  19  6
15  24  25  20  7
14  23  22  21  8
13  12  11  10  9
其实非常简单,只是算法比较难想,我一开始按DP做的,没做出来,后来在发先记忆化搜索可以。
#include<iostream>
#include<cstdio>
using namespace std;
int r,c,ans;
int a[10000][10000],f[10000][10000],dx[5]={0,1,0,-1,0},dy[5]={0,0,1,0,-1};
int search(int x,int y)
{
    int nx,ny,tmp;
    if(f[x][y]>0)
    return f[x][y];
    int t=1;
    for(int i=1;i<=4;i++)
    {
        nx=x+dx[i];
        ny=y+dy[i];
        if(nx<=r&&nx>=1&&ny<=c&&ny>=1&&a[nx][ny]>a[x][y])   
        {
        tmp=search(nx,ny)+1;
        if(tmp>t)
        t=tmp;
        }
    }
        f[x][y]=t;
        return (t);
}
int main()
{
    cin>>r>>c;
    for(int i=1;i<=r;i++)
    for(int j=1;j<=c;j++)
    cin>>a[i][j];
    for(int i=1;i<=r;i++)
    for(int j=1;j<=c;j++)
    {
        f[i][j]=search(i,j);
        if(f[i][j]>ans)
        ans=f[i][j];
    }
    printf("%d",ans);
}

 最后来道难亿点的题——→小木棍

posted @ 2021-03-20 15:16  S_Curry  阅读(64)  评论(0编辑  收藏  举报