目录
[1. 基本概念](#1. 基本概念)
[2. 核心作用](#2. 核心作用)
[3. 复杂度特点](#3. 复杂度特点)
原创内容,多多支持!
一,剪枝简介
1. 基本概念
剪枝就是在搜索过程中,直接剔除掉不必要的搜索分支。因为搜索过程可以看作一棵 "树",剔除分支就像在树上剪掉树枝,因此得名 "剪枝"。
2. 核心作用
剪枝是回溯法的一种重要优化手段。
1)通常先实现一个暴力搜索,再寻找特殊的数学关系或逻辑关系。
2)利用这些关系作为约束,让搜索树变得更浅、更小,从而降低时间复杂度
3. 复杂度特点
剪枝后的算法复杂度通常难以精确计算,它高度依赖具体问题和剪枝策略的有效性。
二,例题详解
例题1:蓝桥杯官网------数字王国之军训排队
代码详解:
1.不剪枝版本,只能通过部分案例
cpp
#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int N=15;
int n,a[N];
vector<int>v[N];
//这是不剪枝版本,直接在递归末尾判断合法性
//cnt表示队伍数量,dep表示当前队员的编号
bool dfs(int cnt,int dep)
{
//当dep==n+1,说明已经判断好所有队员,判断该方案的合法性
if(dep==n+1)
{
for(int i=1;i<=cnt;i++)//枚举所有队伍
{
for(int j=0;j<v[i].size();j++)
{
for(int k=j+1;k<v[i].size();k++)//排序后k位肯定比j位大
{
if(v[i][k]%v[i][j]==0) return false;
}
}
}
return true;
}
//当递归还没完成,尝试将当前队员放入所有队伍中,查看合法性:
for(int i=1;i<=cnt;i++)
{
v[i].push_back(a[dep]);
//递归处理下一个数字,如果,当前递归返回true,说明当前方案可行!
if(dfs(cnt,dep+1)) return true;//注意当前队伍的个数还是cnt不是i
//否则就将它退出,还原队伍
v[i].pop_back();
}
return false;
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
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++)
{
if(dfs(i,1))
{
cout<<i<<'\n';
break;
}
}
return 0;
}
2.剪枝版本
cpp
#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int N=15;
int n,a[N];
vector<int>v[N];
//剪枝版本,在递归中途就判断合法性
//cnt表示队伍数量,dep表示当前队员的编号
bool dfs(int cnt,int dep)
{
//当dep==n+1,说明已经判断好所有队员,判断该方案的合法性
if(dep==n+1)
{
return true;
}
for(int i=1;i<=cnt;i++)
{
//已经排过序了,a[dep]后进,所以a[dep]>=j
//for(const auto &j:v[i]) if(a[dep]%j==0) continue;
//上面这一句错了,这里continue掉的是元素,而不是整个队列!
bool tag=true;
for(const auto &j:v[i]) {if(a[dep]%j==0) tag=false;};
if(!tag) continue;//这样就是跳过的整个队列,表示当前数不能入该队!
v[i].push_back(a[dep]);
if(dfs(cnt,dep+1)) return true;
v[i].pop_back();
}
return false;
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
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++)
{
if(dfs(i,1))
{
cout<<i<<'\n';
break;
}
}
return 0;
}
例题2:蓝桥杯官网------特殊三角形
题目推导:

代码详解:
cpp
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
const int N=1e6+9;
int dep,st,cnt[N],prefix[N],mul,sum;
//dep表示当前层数(要计算的第几条边),st表示当前边的上一条边的长度,
//mul计算边的乘积,sum计算变的长度和,cnt[i]记录三条边乘积为i的数量
void dfs(int dep,int st,int mul,int sum)
{
if(mul>1e6) return;
if(dep==4)//表示3条边已经判断完了,此时到了第四层,记录该合理的方案
{
cnt[mul]++;
return;
}
//当没有判断完,判断下一条边,枚举当前边(下一条边的上一条边)的长度(必须比前一条边长,
//因为三条边长度不同,就当是排好序了):
int up=pow(1e6/mul,1.0/(4-dep))+3;
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<=N;i++) prefix[i]=prefix[i-1]+cnt[i];
int t;cin>>t;
while(t--)
{
int l,r;cin>>l>>r;
cout<<prefix[r]-prefix[l-1]<<'\n';
}
}
例题2:蓝桥杯官网------特殊多边形
题目推导:

代码详解:
cpp
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
const int N=1e5+9;
int n,dep,st,cnt[N],prefix[N],mul,sum;
//dep表示当前层数(要计算的第几条边),st表示当前边的上一条边的长度,
//mul计算边的乘积,sum计算变的长度和,cnt[i]记录三条边乘积为i的数量
void dfs(int dep,int st,int mul,int sum)
{
if(mul>1e5) return;
if(dep==n+1)
{
cnt[mul]++;
return;
}
//当没有判断完,判断下一条边,枚举当前边(下一条边的上一条边)的长度(必须比前一条边长,
//因为三条边长度不同,就当是排好序了):
int up=pow(1e5/mul,1.0/(n-dep+1))+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<=N;i++) prefix[i]=prefix[i-1]+cnt[i];
while(t--)
{
int l,r;cin>>l>>r;
cout<<prefix[r]-prefix[l-1]<<'\n';
}
}