c++ dfs搜索算法——剪枝

目录

一,剪枝简介

[1. 基本概念](#1. 基本概念)

[2. 核心作用](#2. 核心作用)

[3. 复杂度特点](#3. 复杂度特点)

二,例题详解

例题1:蓝桥杯官网------数字王国之军训排队

代码详解:

例题2:蓝桥杯官网------特殊三角形

题目推导:

代码详解:

例题2:蓝桥杯官网------特殊多边形

题目推导:

代码详解:


原创内容,多多支持!

一,剪枝简介

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';
  }
}

最近天气好冷,手都冻僵了,字写的好丑((╬◣д◢))

相关推荐
Thera77729 分钟前
【Linux C++】彻底解决僵尸进程:waitpid(WNOHANG) 与 SA_NOCLDWAIT
linux·服务器·c++
Wei&Yan33 分钟前
数据结构——顺序表(静/动态代码实现)
数据结构·c++·算法·visual studio code
wregjru1 小时前
【QT】4.QWidget控件(2)
c++
浅念-1 小时前
C++入门(2)
开发语言·c++·经验分享·笔记·学习
小羊不会打字1 小时前
CANN 生态中的跨框架兼容桥梁:`onnx-adapter` 项目实现无缝模型迁移
c++·深度学习
Max_uuc1 小时前
【C++ 硬核】打破嵌入式 STL 禁忌:利用 std::pmr 在“栈”上运行 std::vector
开发语言·jvm·c++
近津薪荼1 小时前
dfs专题4——二叉树的深搜(验证二叉搜索树)
c++·学习·算法·深度优先
艾莉丝努力练剑2 小时前
【Linux:文件】Ext系列文件系统(初阶)
大数据·linux·运维·服务器·c++·人工智能·算法
Once_day2 小时前
C++之《程序员自我修养》读书总结(1)
c语言·开发语言·c++·程序员自我修养
Trouvaille ~2 小时前
【Linux】TCP Socket编程实战(一):API详解与单连接Echo Server
linux·运维·服务器·网络·c++·tcp/ip·socket