acwing算法基础03-递归,枚举

cWing 93. 递归实现组合型枚举

1.排序 考虑顺序

  1. 组合 不考虑顺序


参数 -核心

递归 模板 1.指数型 选/不选 2. 排列 -考虑顺序 (判重数组 不知道哪个数有有没有用过)3.组合 不考虑顺序

数据范围 从n个数里选m个数 组合数中间点 取范围

cpp 复制代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

const int N = 30;

int n,m;
int way[N];//表示方案

void dfs(int u,int start){
    //1边界考虑 
    if(u==m+1){
    //已经枚举完最后一个数 u =1 没选 u=2选了一个数字
        for(int i=1;i<=m;i++) printf("%d ",way[i]);
        puts("");//回车换行
        return;
    }
    
    for(int i=start;i<=n;i++){
        way[u]=i;//把当前这个数放入way
        dfs(u+1,i+1);//递归到下一层
        way[u]=0;//回复现场
    }
    
}

int main(){
    scanf("%d%d",&n,&m);
    dfs(1,1);//第一个数开始枚举,第一个数从数字1开始枚举
    return 0;
}

优化 剪 (判断)

假如正在选第u个数 说明已经选了u-1个数

后面可以选start 到n 就算把start到n全部悬赏都不够 剪掉

空的个数 <m

提前退出 -减枝

cpp 复制代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

const int N = 30;

int n,m;
int way[N];//表示方案

void dfs(int u,int start){
    //减掉不合理情况
    if(u+n-start<m) return;
    //1边界考虑 
    if(u==m+1){
    //已经枚举完最后一个数 u =1 没选 u=2选了一个数字
        for(int i=1;i<=m;i++) printf("%d ",way[i]);
        puts("");//回车换行
        return;
    }
    
    for(int i=start;i<=n;i++){
        way[u]=i;//把当前这个数放入way
        dfs(u+1,i+1);//递归到下一层
        way[u]=0;//回复现场
    }
    
}

int main(){
    scanf("%d%d",&n,&m);
    dfs(1,1);//第一个数开始枚举,第一个数从数字1开始枚举
    return 0;
}

AcWing

  1. 带分数

1.暴力

优化:根据题目本身的性质 刚刚没有用到n的信息 暴力枚举abc 自变量只有两个 除法换成乘法

还有n的范围是有限的 只有6位 a<=n 所以 a最大也只有6位 所以可以判断在枚举的过程中是不是大于n 如果a大于n 也可以退出

先枚举a 在之后枚举c 直接算出b 嵌套的树


1.把b里每个数字抠出来看有没有和a c 相同的

memset()

memcpy

1.枚举a

if (a) dfs_c(u,a,0);对于每个枚举的a 都枚举c

if (check(a,c))ans++; 对于每一个ac 判断b

cpp 复制代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>

using namespace std;

const int N = 30;
bool st[N], backup[N]; // st保证原样,backup用来修改
int ans;
int n;

bool check(int a, int c) {
    // 如果 a 为 0,直接返回 false
    if (!a) return false;

    int b = (n - a) * c;  // 修正公式
    if (b <= 0) return false;  // b 应该大于零才是有效的

    // 逐位检查 b 的数字
    memcpy(backup, st, sizeof st);
    while (b) {
        int x = b % 10;  // 取个位
        b /= 10;  // 删个位
        if (!x || backup[x]) return false;  // 不能有重复或 0
        backup[x] = true;
    }

    // 检查所有数字 1~9 是否都有
    for (int i = 1; i <= 9; i++) {
        if (!backup[i]) return false;
    }

    return true;
}

void dfs_c(int u, int a, int c) {
    if (u == n) return;
    if (check(a, c)) ans++;
    for (int i = 1; i <= 9; i++) {
        if (!st[i]) {
            st[i] = true;
            dfs_c(u + 1, a, c * 10 + i); // 修正 c 的传递
            st[i] = false;
        }
    }
}

void dfs_a(int u, int a) {
    if (a >= n) return;
    if (a) dfs_c(u, a, 0);  // 传递给 dfs_c
    for (int i = 1; i <= 9; i++) {
        if (!st[i]) {
            st[i] = true;
            dfs_a(u + 1, a * 10 + i);  // 递归选择数字
            st[i] = false;
        }
    }
}

int main() {
    cin >> n;
    dfs_a(0, 0);  // 当前已经用了多少数字
    cout << ans << endl;
    return 0;
}
cpp 复制代码
1. 全局变量和初始化
cpp
const int N = 30;
bool st[N], backup[N];  // st 用来标记数字是否被使用,backup 用于在 check 时备份
int ans;
int n;
st[N]:布尔数组,st[i]表示数字i是否已被使用。通过这个数组来确保每个数字只使用一次。
backup[N]:用来在check函数中备份st数组的状态,确保在递归调用过程中状态不会互相干扰。
ans:存储满足条件的方案总数。
n:目标数字,输入的数字。dfs_a和dfs_c都会用到这个目标。
2. check函数:
cpp
bool check(int a, int c) {
    if (!a) return false;  // 如果 a 为 0,则返回 false
    int b = (n - a) * c;  // 计算 b
    if (b <= 0) return false;  // b 应该大于 0

    memcpy(backup, st, sizeof st);  // 备份 st 数组的当前状态
    while (b) {
        int x = b % 10;  // 取 b 的个位
        b /= 10;  // 移除个位
        if (!x || backup[x]) return false;  // 不能有重复或是 0
        backup[x] = true;  // 标记数字 x 已经使用过
    }

    for (int i = 1; i <= 9; i++) {
        if (!backup[i]) return false;  // 检查 1 到 9 的所有数字是否都有
    }

    return true;  // 如果满足条件,返回 true
}
check函数的目的是验证给定的a和c是否满足题目中要求的条件。
首先检查 a 是否为 0,如果是,直接返回 false。
然后计算 b = (n - a) * c,这个计算的目的是生成一个新的数字 b,并检查 b 的每一位数字。
如果b有重复的数字,或者其中包含数字0,check返回 false。
使用 backup数组来备份当前的 st 数组状态,避免递归过程中的状态冲突。
最后,确认 1~9 之间的数字是否都被使用过(即 backup[i] == true)。
3. dfs_c函数:
cpp
void dfs_c(int u, int a, int c) {
    if (u == n) return;  // 如果深度达到目标,结束递归
    if (check(a, c)) ans++;  // 如果满足条件,增加答案数
    for (int i = 1; i <= 9; i++) {
        if (!st[i]) {
            st[i] = true;  // 标记数字 i 已经被使用
            dfs_c(u + 1, a, c * 10 + i);  // 递归调用,生成新的 c
            st[i] = false;  // 回溯,取消标记
        }
    }
}
dfs_c 是一个递归函数,用来生成数字的组合。
u 是当前递归的深度。
a 是在递归过程中生成的数字的一部分(原始的部分)。
c 是用来拼接数字的另一部分。
u == n 时,表示递归已经达到目标的深度,直接返回。
check(a, c) 用来检查当前的数字组合是否满足条件。如果满足,答案数量ans增加。
接着通过循环遍历 1~9 之间的数字,逐步生成数字 c,并递归调用dfs_c继续生成。
st[i] = true 标记数字 i 已经使用过。
st[i] = false 是回溯的操作,表示取消对数字 i 的使用。
4. dfs_a函数:
cpp
void dfs_a(int u, int a) {
    if (a >= n) return;  // 如果 a 超过 n,停止递归
    if (a) dfs_c(u, a, 0);  // 如果 a 不为 0,则调用 dfs_c
    for (int i = 1; i <= 9; i++) {
        if (!st[i]) {
            st[i] = true;  // 标记数字 i 已使用
            dfs_a(u + 1, a * 10 + i);  // 递归调用,继续生成 a
            st[i] = false;  // 回溯
        }
    }
}
dfs_a 是另一个递归函数,用来生成数字 a 的组合。它的作用是生成一个基础数字 a,然后通过调用 dfs_c 生成与其相关的数字 c。
a >= n 时,停止递归。
如果 a 非零,则调用 dfs_c,将当前的 a 和 0(空的 c)传递给 dfs_c,生成相关的组合。
接着用递归生成所有 a 的可能组合,并在每次递归后回溯,确保每个数字只使用一次。
5. main函数:
cpp
int main() {
    cin >> n;  // 输入目标数字 n
    dfs_a(0, 0);  // 从 0 开始递归生成所有组合
    cout << ans << endl;  // 输出答案数量
    return 0;
}
main函数首先读取输入的数字 n。
然后调用 dfs_a(0, 0) 开始递归,0 表示从数字 a 开始为空。
最后输出 ans,即满足条件的组合数量。

递归:f(n) 调用f(1) f(2)

递推f(1) f(2) 返回到f(n)


滚动数组

相关推荐
xiaoshiguang34 小时前
LeetCode:222.完全二叉树节点的数量
算法·leetcode
爱吃西瓜的小菜鸡4 小时前
【C语言】判断回文
c语言·学习·算法
别NULL4 小时前
机试题——疯长的草
数据结构·c++·算法
TT哇4 小时前
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
java·算法·蓝桥杯
yuanbenshidiaos5 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习5 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA6 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
chengooooooo6 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc6 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
游是水里的游7 小时前
【算法day20】回溯:子集与全排列问题
算法