88888888y

导航

 

题目:https://www.luogu.com.cn/problem/P1120

题目描述:给你一堆木棍,拼出一些长度相等的木棍,问这个“长度”最小可以是几

题目思路:还是深搜与剪枝

首先来一个基本思路:

从小到大枚举答案再爆搜

开始剪枝:

优化1:

由于题目中说“长度不大于50的木棍”

所以在输入小木棍的时候,长度大于50的可以直接扔掉

优化2:

原始木棍再短也短不过小木棍的长度,所以答案所在范围的上界就是所有小木棍中长度最长的那个

再想,如果原始木棍只有1根,那不就是把所有的木棍加起来吗?所以答案所在范围的下界就是所有小木棍的长度之和

而后,如果原始木棍有2根,那就是所有小木棍的长度之和对半分,也就是范围下界对半分

所以我们的下界可以定为(所有小木棍的长度之和)/2,如果这都没有,那就只能是把所有木棍加起来的那种了

优化3:

如果所有小木棍的长度之和无法整除一个假定答案,那么这个答案是无论如何也无法实现的

这就可以再刷下来一批

优化4:

由于是从小到大枚举,所以搜到一个可行的答案可以直接输出然后再见

注意了!!!!优化5-8是重点!!!!

优化5:

前面的木棍我们已经看过了,所以我们没有必要再去看它们(仅限于同一根假定原始木棍中)

所以设计一个p作为这次搜索开始的起点

优化6:

如果当前剩余的长度与当前搜到的小木棍的长度相等

这就是一个运气爆棚的方案

要是还没过,那就可以再见了

优化7:

如果当前剩余的长度与假定原始木棍的长度相等

那么这货兜兜转转还是会回到优化6中出现的情况、

玩nm!

优化8:

如果这个木棍不行,那么与它同样长度的木棍都不行

全找出来,并且全踢掉(这也是我排序的理由)

代码如下

#include<bits/stdc++.h>
using namespace std;
int n,a[66],i,sum=0,l,d,v[66];
//a是一堆木棍,v是v[i]木棍是否被用过 
bool cmp(int x,int y){//由大到小排序 
	return x>y;
}
bool dfs(int now,int p,int sti){
    //now是当前正在拼的木棍的最小长度 
    //p是我们待会儿搜索木棍的起点 
	//sti是已经拼出来的木棍数量
	if(sti==d){//这个数量达到了预期 
		return 1;//此方案可行,返回“真” 
	}
	if(now==0){//如果这根木棍恰好拼完 
		if(dfs(l,1,sti+1)){//拼完的木棍(sti+1)
		//再回到预期木棍长度和数组起点 
		//如果返回了“真”证明此方案可行 
			return 1;//返回“真” 
		}
	}
	for(int i=p;i<=n;++i){//优化5 
		if(v[i]==0&&a[i]<=now){//没有用过且拼成之后的木棍没有过长 
			v[i]=1;//设定“已使用” 
			if(dfs(now-a[i],i+1,sti)){//继续递归
			//如果这个递归返回“真”,那么此方案可行 
				return 1;//返回“真” 
			}
			v[i]=0;//如果没成就把木棍吐回来 
			if(now==a[i]||now==l){//优化6,7 
				break;
			}
			while(a[i+1]==a[i]){//优化8 
				i++;
			}
		}
	}
	return 0;
}
int main(){
	scanf("%d",&n);
	for(i=1;i<=n;++i){
		scanf("%d",&a[i]);
		if(a[i]>50){//优化1 
			i--;
			n--;
		}
		else{
			sum+=a[i];//找上界 
		}
	}
	sort(a+1,a+1+n,cmp);//找下界 
	for(i=a[1];i<=sum/2;++i){//优化2 
		if(sum%i==0){//优化3 
			l=i;
			d=sum/i;
			if(dfs(l,1,0)){ 
				printf("%d",l);
				return 0;//优化4 
			}
		}
	}
	printf("%d",sum);
	return 0;//再见 
}
总结:经典剪枝题
多读读5-8的优化,会有很大收获


 

posted on 2022-03-20 11:42  88888888y  阅读(44)  评论(0)    收藏  举报