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

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

相关推荐
CSDN_RTKLIB2 小时前
【std::string】find函数
c++·stl
十五年专注C++开发2 小时前
浅谈CPU中的SIMD
c++·cpu·代码优化·simd
Yu_Lijing2 小时前
基于C++的《Head First设计模式》笔记——状态模式
c++·笔记·设计模式
顶点多余3 小时前
静态链接 vs 动态链接,静态库 vs 动态库
linux·c++·算法
AI视觉网奇3 小时前
ue5 开发 web socket server 实战2026
c++·学习·ue5
王老师青少年编程3 小时前
2024年3月GESP真题及题解(C++八级): 接竹竿
c++·题解·真题·gesp·csp·八级·接竹竿
偷星星的贼113 小时前
C++中的访问者模式实战
开发语言·c++·算法
雾岛听蓝4 小时前
红黑树深度解析:设计原理与实现逻辑
c++
gjxDaniel4 小时前
A+B问题天堂版
c++·算法·字符串·字符数组