将搜索过程中一些不必要的部分直接剔除。剪枝是回溯法的一种优化手段,先写一个暴力搜索,然后找到某些特殊的数字关系或者逻辑关系,通过约束来降低时间复杂度。
蓝桥 知识点:搜索+剪枝
数字王国开学了,它们也和我们人类一样有开学前的军训,现在一共有 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<