3.深搜的剪枝技巧

本文介绍了深度搜索的优化策略,包括优化搜索顺序、排除等效冗余、可行性剪枝、最优性剪枝以及记忆化搜索。通过实例分析了这些技巧在数的划分和生日蛋糕问题中的应用,有效提高了搜索效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简直主要包括:

1.优化搜索顺序

  • 在很多时候,不同的搜索顺序往往会产生不同形状的搜索树,就需要尽可能的优化

2.排除等效冗余

  • 有的时候我们可以从不同的分支到达同一个结果,那么这样的分支显然就只需要通过一次就可以了

3.可行性剪枝

  • 简单的来说就是尽可能早的发现不可能的情况并进行剪枝,也就是在判断的时候进行优化

4.最优性剪枝

  • 在最优化问题的过程中,如果当前的代价已经小于目前搜索到的最优解,显然就不用继续下去了,那么我们可以停止搜索,直接回溯

5.记忆化

  • 可以消耗空间来换取时间,就是记录每个状态的搜索结果,这样在遍历时就可以直接查询了

数的划分

http://ybt.ssoier.cn:8088/problem_show.php?pid=1440

这题还是挺简单的,如果不进行处理只搜索,那么运行的会很慢,但是控制好每个枚举点的上下界,就会使运行效率便快很多了。

 

#include<iostream>
using namespace std;
int n,m,a[205],ans=0;
void dfs(int k)
{
	int i;
	if(k==0) return;
	if(k==m)
	{
		if(n>=a[k-1]) ans++;
        return;
	}
	for(i=a[k-1];i<=n/(m-k+1);i++)//处理好上下界
	{
		a[k]=i;
		n-=i;
		dfs(k+1);
		n+=i;
	}
}
int main(){
	cin>>n>>m;
	a[0]=1;
	dfs(1);
	cout<<ans<<endl;
	return 0;
}

 生日蛋糕

http://ybt.ssoier.cn:8088/show_source.php?runid=7398406

我用了可行性和最优性剪枝来优化了搜索

  • 可行性剪枝  ——搭建过程中预见无法继续搭建,将要超过体积,将不可能达到体积 3个可行性剪
  • 最优性剪枝——搭建过程中发现已建好的面积已经超过目前求得的最优表面积,或建完后面积一定会超过目前最优表面积,则停止搭建
    #include <iostream>
    #include <cmath>
    using namespace std;
    
    int n,m;
    int minArea = 1 << 30;
    int minA[22] = {0},minV[22] = {0};
    int area = 0;
    
    int maxVJudge(int M, int r, int h);
    void Dfs(int v,int N,int r,int h);
    
    int main()
    {
        cin>>n,m;
        for(int i = 1; i <= m; i++)
        {
            minA[i] = minA[i-1] + 2 * i * i;
            minV[i] = minV[i-1] + i * i * i;
        }
        int MaxH = (n - minV[m-1]) / (m * n) + 1;
        int MaxR = sqrt((n - minV[m-1]) / m) + 1;
        Dfs(n,m,MaxR,MaxH);
        if(minArea == 1 << 30)
            cout<< 0 <<endl;
        else
            cout<< minArea <<endl;
        return 0;
    }
    
    int maxVJudge(int M, int r, int h)
    {
        int maxV = 0;
        for(int i = 0; i < M; i++)
            maxV += (r-i) * (r-i) *(h-i);
        return maxV;
    }
    
    void Dfs(int v,int n,int r,int h)
    {
        if(n == 0)
        {
            if(v)
                return;
            else
            {
                minArea = min(minArea,area);
                return;
            }
        }
        if(v<= 0||minV[n] > v||maxVJudge(n,r,h) < v) // 可行性剪枝
            return ;
    
        if(minA[n]+area >= minArea) // 最优性剪枝
            return;
        for(int rr = r; rr >= n; --rr)
        {
            if(n == M)                         
                area = rr * rr;
            for(int hh = h; hh >= n; --hh)
            {
                area += 2 * rr * hh;
                Dfs(v-rr*rr*hh,n-1,rr-1,hh-1);
                area -= 2 * rr * hh;
            }
        }
    }

    3.小木棍

  • 剪切的很多

  • #include <iostream>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    int n,sum=0,a[60],len,m,temp,minn=0,used[60];
    bool cmp(int x,int y)
    {
    	return x>y;
    }
    void read()
    {
    	cin>>n;
    	for (int i=0;i<n;i++)
    	{
    		cin>>a[i];
    		sum+=a[i];
        }
        sort(a,a+n,cmp);                 //剪枝3,从大到小排序,长木棍的灵活性差,先排长木棍 
    }
    void dfs(int k,int last,int rest)   //第k根木棍,last为第k根上一节木棍编号,rest为第k根木棍还需要的长度 
    {
    	if (k==m) {temp=1;return;}
    	if (rest==0)                    //开始下一根木棍 
    	{
    		int i;
    		for (i=0;i<n;i++)                       //剪枝4
    		    if (!used[i]) {used[i]=1;break;}    //找到没有用过最长的木棍作为第一节 
    		dfs(k+1,i,len-a[i]);
    	}
    	for (int i=last+1;i<n;i++)      //剪枝5、7,因为已经排好序了,所以它的下一个不大于它的长度 
    	{
    		int j;
    		if (!used[i]&&rest>=a[i])   //木棍未被访问过,且比需要的木棍长度小
    		{
    			used[i]=1;
    			dfs(k,i,rest-a[i]); 
    			used[i]=0;              //回溯 
    			j=i;                    //记录当前的木棍的编号 
    			while (i<n-1&&a[i]==a[j]) i++;   //剪枝6,防止相同长度的木棍搜索多次 
    			if (i==n-1)return ;               
    		} 
    	}
    }
    void solve()
    {
    	for (int i=a[0];i<=sum;i++)        //剪枝2
    		if (sum%i==0)                  //剪枝1,sum能被原木棍的长度整除 
    		{
    			memset(used,0,sizeof(used));
    			len=i;
    			used[0]=1;
    			temp=0;                    //记录有没有得到答案 
    			m=sum/i;                   //木棍的根数 
    			dfs(1,0,len-a[0]);         
    			if (temp) {cout<<len<<endl;break;}
    		}
    }
    int main ()
    {
    	read();
    	solve();
    	return 0;
    }
    

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值