算法竞赛备赛——【搜索】剪枝

剪枝

将搜索过程中一些不必要的部分直接剔除。剪枝是回溯法的一种优化手段,先写一个暴力搜索,然后找到某些特殊的数字关系或者逻辑关系,通过约束来降低时间复杂度。

例题

数字王国之军训排队

蓝桥 知识点:搜索+剪枝

问题描述

数字王国开学了,它们也和我们人类一样有开学前的军训,现在一共有 n名学生,每个学生有自己的一个名字 ai(数字王国里的名字就是一个正整数,注意学生们可能出现重名的情况),此时叛逆教官来看了之后感觉十分别扭,决定将学生重新分队。

排队规则为:将学生分成若干队,每队里面至少一个学生,且每队里面学生的名字不能出现倍数关系(注意名字相同也算是倍数关系)。

现在请你帮忙算算最少可以分成几队?

例:有 4 名学生 (2,3,4,4),最少可以分成 (2,3)、(4)、(4) 共 3队。

输入格式

第一行包含一个正整数 n,表示学生数量。

第二行包含 n 个由空格隔开的整数,第 i个整数表示第 i个学生的名字 ai。

输出格式

输出共 1 行,包含一个整数,表示最少可以分成几队。

样例输入

4
2 3 4 4

样例输出

3

评测数据规模

对于所有评测数据,1≤n≤10,1≤ai≤10^5。

#include
using namespace std;
const int N=15;
using ll=long long;

int n,a[N];

vector v[N]; //模拟分成的队伍 

bool dfs(int cnt,int dep){
	if(dep==n+1){//n个数已分好队伍 
		return true;
	} 
	//枚举每个人所属队伍 
	for(int i=1;i<=cnt;++i){
		int tag=0;
		for(const auto &j:v[i]){	//添加的时候就判断,即剪枝 
			if(a[dep]%j==0) tag=1;
		}
		if(tag) continue;
		v[i].push_back(a[dep]);		
		if(dfs(cnt,dep+1)) return true;	//判断
		v[i].pop_back(); 		//回溯
	} 
	return false;
}

int main(){
	cin>>n;
	for(int i=1;i<=n;++i) cin>>a[i];
	sort(a+1,a+1+n);
	for(int i=1;i<=n;++i){	//枚举分成i队 
		if(dfs(i,1)){	//分成i队能成功 
			cout<

特殊的三角形

蓝桥 知识点:搜索+剪枝

问题描述

假设一个三角形三条边为 a、b、c,定义该三角形的值 v=a×b×c。

现在有 t个询问,每个询问给定一个区间 [l,r],问有多少个三条边都不相等的三角形的值 v在该区间范围内。

输入格式

第一行包含一个正整数 t,表示有 t个询问。

接下来 t行,每行有两个空格隔开的正整数 l、r,表示询问区间 [l,r]。

输出格式

输出共 t 行,第 i 行对应第 i 个查询的三角形个数。

样例输入

4
1 10
30 50
60 200
200 400

样例输出

0
1
18
32

说明

在样例中,第二个区间 [30,50]的合法三角形的边长为 2、4、5。

评测数据规模

对于所有评测数据,1≤t≤105,1≤l≤r≤10^6。

#include
using namespace std;
const int N=1e6+9;
using ll=long long;

int cnt[N],prefix[N];

//dep表示已经乘了的个数 st表示上一个数(限制本次选数的范围从st+1开始) mul为乘积  sum为已选的边的和 
void dfs(int dep,int st,int mul,int sum){
	//剪枝
	if(mul>1e6) return ;
	if(dep==4){
		cnt[mul]++;
		return ;
	} 	//公式推导 mul*x^(n-dep+1)<=1e6 ==> x<=(1e6/mul)^(1/n-dep+1) 
	int up=pow(1e6/mul,1.0/(4-dep))+3;//当前x取值的最大范围 [st+1,up] 
	for(int i=st+1;i<(dep==3?sum:up);++i){//第三边小于前两边之和 
		dfs(dep+1,i,mul*i,sum+i);
	}
}

int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	dfs(1,0,1,0);
	for(int i=1;i<=1e6;++i)
		prefix[i]=prefix[i-1]+cnt[i];
	
	int t; cin>>t;
	while(t--){
		int l,r;cin>>l>>r;
		cout<

特殊的多边形

蓝桥 知识点:搜索+剪枝

问题描述

假设一个 n 边形 n 条边为 a1,a2,a3,⋯,an,定义该 n 边形的值 v=a1×a2×a3×⋯×an。

定义两个 n边形不同是指至少有一条边的长度在一个 n 边形中有使用而另一个 n边形没有用到,如 n 边形 (3,4,5,6)和 (3,5,4,6)是两个相同的 n 边形,(3,4,5,6)和 (4,5,6,7)是两个不相同的 n 边形。

现在有 t和 n,表示 t 个询问并且询问的是 n 边形,每个询问给定一个区间 [l,r],问有多少个 n 边形(要求该 n 边形自己的 n 条边的长度互不相同)的值在该区间范围内。

输入格式

第一行包含两个正整数 t、n,表示有 t 个询问,询问的是 n 边形。

接下来 t行,每行有两个空格隔开的正整数 l、r,表示询问区间 [l,r]。

输出格式

输出共 t行,第 i行对应第 i 个查询的 n 边形个数。

样例输入

4 3
1 10
30 50
60 200
200 400

样例输出

0
1
18
32

说明

在样例中,第二个区间 [30,50]的合法三边形(三角形)的边长为 2、4、5。

评测数据规模

对于所有评测数据,1≤t≤105,3≤n<10,1≤l≤r≤10^5。

#include
using namespace std;
const int N=1e5+9;
using ll=long long;

int n,cnt[N],prefix[N];

//dep表示已经乘了的个数 st表示上一个数(限制本次选数的范围从st+1开始) mul为乘积  sum为已选的边的和 
void dfs(int dep,int st,int mul,int sum){
	//剪枝1
	if(mul>1e5) return ;
	if(dep==n+1){
		cnt[mul]++;
		return ;
	} 
	//剪枝2
	int up=pow(1e5/mul,1.0/(n+1-dep))+3;//当前x取值的最大范围 [st+1,up] 
	//剪枝3 递增避免重复 
	for(int i=st+1;i<(dep==n?sum:up);++i){//第三边小于前两边之和 
		dfs(dep+1,i,mul*i,sum+i);
	}
}

int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);	
	int t; cin>>t>>n;
	dfs(1,0,1,0);
	for(int i=1;i<=1e5;++i)
		prefix[i]=prefix[i-1]+cnt[i];	
	while(t--){
		int l,r;cin>>l>>r;
		cout<

你可能感兴趣的:(算法竞赛备赛,算法,剪枝,c++,蓝桥杯,数据结构)