洛谷P1120 小木棍 [数据加强版](剪枝技巧!)
题目链接
参考自:
https://www.luogu.org/blog/user30688/solution-p1120
解题思路:
这题关键在于剪枝。
 (1)逆序排列整个序列,从最大的值往回搜
 (2)dfs每次从当前点
    
     
      
       
        n
       
       
        o
       
       
        w
       
      
      
       now
      
     
    now开始搜,而不是每次从最开始的点
    
     
      
       
        a
       
       
        [
       
       
        1
       
       
        ]
       
      
      
       a[1]
      
     
    a[1]开始搜
 (3)在正向的搜索过程中,如果当前积累的长度
    
     
      
       
        n
       
       
        u
       
       
        m
       
      
      
       num
      
     
    num与平均值
    
     
      
       
        a
       
       
        v
       
       
        e
       
       
        r
       
      
      
       aver
      
     
    aver的差比整个序列的最小值还小,那么一定不能凑成一组,
    
     
      
       
        r
       
       
        e
       
       
        t
       
       
        u
       
       
        r
       
       
        n
       
      
      
       return
      
     
    return。
 (4)技巧部分还在回溯过程:回溯过程中如果找到一组(即
    
     
      
       
        n
       
       
        u
       
       
        m
       
       
        +
       
       
        a
       
       
        [
       
       
        i
       
       
        ]
       
       
        =
       
       
        =
       
       
        a
       
       
        v
       
       
        e
       
       
        r
       
      
      
       num+a[i]==aver
      
     
    num+a[i]==aver)或者回到上一层开始的地方(即
    
     
      
       
        n
       
       
        u
       
       
        m
       
       
        =
       
       
        =
       
       
        0
       
      
      
       num==0
      
     
    num==0),表示当前组不符合,
    
     
      
       
        r
       
       
        e
       
       
        t
       
       
        u
       
       
        r
       
       
        n
       
      
      
       return
      
     
    return。
 (5)其次,回溯过程中,如果有很多重复的数字,直接跳过(即
    
     
      
       
        w
       
       
        h
       
       
        i
       
       
        l
       
       
        e
       
       
        (
       
       
        a
       
       
        [
       
       
        i
       
       
        ]
       
       
        =
       
       
        =
       
       
        a
       
       
        [
       
       
        i
       
       
        +
       
       
        1
       
       
        ]
       
       
        )
       
       
       
        i
       
       
        +
       
       
        +
       
       
        ;
       
      
      
       while(a[i]==a[i+1]) \quad i++;
      
     
    while(a[i]==a[i+1])i++;)。
#include <iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[70],vis[70];
int sum=0,maxa=-1,aver=0,cnt=0;
int n,flag=0,m=0;
bool cmp(int x,int y){
	return x>y;//逆序 
}
void dfs(int step,int num,int now){//从now开始,而不是每次从1开始,节省时间 
//step表示搜索的组数,num表示每一组积累的长度,now表示现在正遍历的点
//	printf("%d %d\n",step,num);
    if(step==cnt||flag){
    	printf("%d\n",aver),exit(0);//退出 
        flag=1;
        return;
    }
    if(num==aver){
        dfs(step+1,0,1);//从头开始 
        return;
    }
    if(aver-num<a[n])  return;//这步是说明在前面的两步判断之后,如果我们的平均值减去这个num的值
                               //比整个序列的最小值还小,那一定不能再凑成新的一组了。 
    for(int i=now;i<=n;i++){
        if(!vis[i]&& num+a[i]<=aver){
        //除了保证i没有被访问过,还要保证num+当前的a[i]小于平均值,才能进入下一步的dfs 
        	vis[i]=1;
            dfs(step,num+a[i],i+1);//开始下一步 
			vis[i]=0;             //回溯过程的处理非常关键 
			if(num+a[i]==aver) 
				return;//回溯找到一组,不可行
			if(num==0)  
				return;//回溯到上一层最开始的地方,不可行 
            while(a[i]==a[i+1]) 
            	i++;//回溯时消除重复的值 (1)
        }
    }
}
int main(int argc, char** argv) {
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int x;
        scanf("%d",&x);
        if(x>50){
            continue;
        }
        a[++m]=x;
        if(a[m]>maxa) maxa=a[m];//最大值 
        sum+=a[m];
    }
    n=m;
    sort(a+1,a+n+1,cmp);//逆序排列,从最大值往回找 
    
    for(int i=maxa;i<=sum;i++){
        if(sum%i==0){
            aver=i,cnt=sum/i;
            dfs(0,0,1);
            if(flag) break;
        }
    }
    printf("%d\n",aver);
    return 0;
}
                
                    
                
                
            
        
浙公网安备 33010602011771号