当前位置: 首页 > article >正文

《从入门到精通:蓝桥杯编程大赛知识点全攻略》(二)-递归实现组合型枚举、带分数问题

在算法和编程中,组合型枚举问题常常出现在许多实际应用中。特别是在涉及到有限的选择、约束条件和分配问题时,递归算法能够以非常高效的方式求解这些问题。递归不仅提供了一个直观的思路,还能使得复杂的问题简化为多个子问题,从而逐步解决。

目录

前言

递归实现组合型枚举

算法思路

代码如下

带分数

算法思路

代码如下

总结


前言

在算法和编程中,组合型枚举问题常常出现在许多实际应用中。特别是在涉及到有限的选择、约束条件和分配问题时,递归算法能够以非常高效的方式求解这些问题。递归不仅提供了一个直观的思路,还能使得复杂的问题简化为多个子问题,从而逐步解决。

在这篇博客中,我们将深入探讨递归在组合型枚举中的应用,特别是带分数的情况。我们将通过一个经典的递归处理问题——带分数问题来展示如何利用递归实现枚举过程。

无论您是初学者还是已经有一定算法基础的开发者,掌握递归的思维方式和技巧,将帮助您在解决复杂问题时更加得心应手,尤其是在需要枚举大量组合的情况下。


递归实现组合型枚举

从 1∼n 这 n个整数中随机选出 m 个,输出所有可能的选择方案。

输入格式

两个整数 n,m ,在同一行用空格隔开。

输出格式

按照从小到大的顺序输出所有方案,每行 1 个。

首先,同一行内的数升序排列,相邻两个数用一个空格隔开。

其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如 1 3 5 7 排在 1 3 6 8 前面)。

数据范围

n>0
0≤m≤n
n+(n−m)≤25

输入样例:

5 3

输出样例

1 2 3 
1 2 4 
1 2 5 
1 3 4 
1 3 5 
1 4 5 
2 3 4 
2 3 5 
2 4 5 
3 4 5 

算法思路

组合是从一组元素中选择若干个元素,不考虑顺序。 

排列是从一组元素中选择若干个元素,考虑顺序

如何从排列的过程中得到组合所需的数列避免重复?

我们可以人为的指定一种顺序,即数列的数字是从小到大排序,这样就可以避免获得重读数列。

 用整型数组way来接收数列,先找到递归的出口,当当前位置u大于m个位置时(u > m),直接循环打印way数组,即可得到数列。当位置未填满时(u < m),我们可以知道当前位置最小为start,此时我们就可以从start一直枚举到最后一个数,然后让当前位置的数列等于此时的i即(way[u] = i),接着递归的处理下一个位置dfs(u+1,i+1),再恢复现场即way[u] =  0即可。

代码如下



import java.io.*;

public class Main {
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st = new StreamTokenizer(br);
    static int[] way = new int[30];
    static int n,m;


    public static void main(String[] args)throws IOException {
        n = nextInt();
        m = nextInt();

        dfs(1,1);
        pw.flush();
    }
    /*
    u表示当前枚举的位置
    start表示当前最小可以从哪枚举
     */
    public static void dfs(int u,int start){
        if(u > m){
            for(int i = 1;i <= m;i++){
                pw.print(way[i]+" ");
            }
            pw.println();
            return;
        }
        for(int i = start;i <= n;i++){
            way[u]=i;
            dfs(u+1,i+1);
            //将原位置恢复
            way[u]=0;
        }
    }

    public static int nextInt()throws IOException {
        st.nextToken();
        return (int)st.nval;
    }
}

当然,还可以进行剪枝优化

package AcWingLanQiao;

import java.io.*;

public class 递归实现组合型枚举 {
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st = new StreamTokenizer(br);
    static int[] way = new int[30];
    static int n,m;


    public static void main(String[] args)throws IOException {
        n = nextInt();
        m = nextInt();

        dfs(1,1);
        pw.flush();
    }
    /*
    u表示当前枚举的位置
    start表示当前最小可以从哪枚举
     */
    public static void dfs(int u,int start){
        //剪枝
        if(u - 1 + n - start + 1 < m){
            return;
        }
        if(u > m){
            for(int i = 1;i <= m;i++){
                pw.print(way[i]+" ");
            }
            pw.println();
            return;
        }
        for(int i = start;i <= n;i++){
            way[u]=i;
            dfs(u+1,i+1);
            //将原位置恢复
            way[u]=0;
        }
    }

    public static int nextInt()throws IOException {
        st.nextToken();
        return (int)st.nval;
    }
}

 就是当前位置是u,那么说明前u - 1个位置已经排好了,那么剩下还有 n - start + 1个数,如果 u - 1 + n - start + 1个数还是不够m即(u + n - start < m),那么就说明一定是无解情况,可以减小递归次数。

带分数

100 可以表示为带分数的形式:100=3+69258/714

还可以表示为:100=82+3546/197

注意特征:带分数中,数字 1∼9 分别出现且只出现一次(不包含 0)。

类似这样的带分数,100 有 11 种表示法。

输入格式

一个正整数。

输出格式

输出输入数字用数码 1∼9 不重复不遗漏地组成带分数表示的全部种数。

数据范围

1\leqslant N < 10^{6}

算法思路

我们可以通过dfs来对9个数字进行全排列得到所有的数字情况,得到每一种情况,然后再来枚举a b c的位数,因为知道a和b的位数,那么c的位数就知道了 。然后对应每一种情况来计算等式是否成立,成立就说明该情况符合。

用整型数组arr来记录结果,布尔类型数组flag来表示1-9那位数组是否被用过,true表示已被使用,false表示未被使用。全排列的详情可参考https://mp.csdn.net/mp_blog/creation/editor/144703235

这篇博客,最重要的就是通过双层循环来枚举a、b、c的位数,比如for循环下标i从1到9,那么a的位数就是从1到i,在嵌套一层循环j从i+1到9,b的位数就是从i+1到 j,最后c位数就是从j+1到9。其中知道位数,数组转换成整数只需要初始化一个sum = 0,然后每遍历一位让sum * 10 + arr[i]就可以得到对应的整数。

代码如下

暴力做法:直接枚举3个数对应的位数

package AcWingLanQiao;

import java.io.*;

public class 带分数 {
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st = new StreamTokenizer(br);
    static boolean[] flag = new boolean[20];
    static int[] arr = new int[20];
    static int target;
    //统计次数
    static int res = 0;
    public static void main(String[] args) throws IOException {
        target = nextInt();
        dfs(1);
        pw.println(res);
        pw.flush();
    }
    //暴力做法
    public static void dfs(int u){
        if(u > 9){
            for(int i = 1;i <= 9;i++){
                for(int j = i+1;j <= 9;j++){
                    int a = cal(1,i);
                    int b = cal(i+1,j);
                    int c = cal(j+1,9);
                    if(target * c == c * a + b){
                        res++;
                    }
                }
            }
            return;
        }
        for(int i = 1;i <= 9;i++){
            if(!flag[i]){
                arr[u] = i;
                flag[i] = true;
                dfs(u+1);
                flag[i] = false;
            }
        }
    }
    public static int cal(int a,int b){
        int sum = 0;
        for(int i = a;i <= b;i++){
            sum = sum * 10 + arr[i];
        }
        return sum;
    }

    public static int nextInt()throws IOException {
        st.nextToken();
        return (int)st.nval;
    }
}

总结

递归算法的一个显著优点是其代码简洁性和直观性,尤其在需要处理大量组合问题时,通过递归能有效减少代码复杂度,提升开发效率。同时,通过优化递归的剪枝策略,我们可以大大减少无效计算,提升算法的性能。

递归不仅仅是解决简单组合问题的工具,更是一种非常强大的解决思路,适用于各种具有层次结构和分治性质的问题。

总之,递归是一种强大的编程工具,能够帮助我们在面对组合型枚举问题时,通过精妙的分解方法高效地找到所有可能的解。在带分数的约束下,我们更能体现递归算法的优势,使得复杂问题变得清晰且易于解决。


http://www.kler.cn/a/470778.html

相关文章:

  • 调整Python+Pytest+Allure+Yaml+Pymysql框架中需要执行的用例顺序
  • 少儿编程学习路径:分阶段成长与进阶指南
  • Vue项目中的问题汇总(持续更新中)
  • 计算机网络 (27)IP多播
  • 《新概念模拟电路》-电流源电路
  • 模型 九屏幕分析法
  • libaom 源码分析线程结构
  • uni-app 页面生命周期及组件生命周期汇总(Vue2、Vue3)
  • 特征点检测与匹配——MATLAB R2022b
  • 2025资源从哪里来!
  • vue3-dom-diff算法
  • Postman接口测试02|接口用例设计
  • 云原生周刊:K8s 生态系统的五大趋势预测
  • IDEA中Lombok不能使用,找不到get方法
  • 乾元通渠道商中标玉溪市自然灾害应急能力提升项目
  • 【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
  • Flink-CDC 全面解析
  • 【pytorch-lightning】架构一览
  • 复杂园区网基本分支的构建
  • 工控主板ESM7000/6800E支持远程桌面控制
  • GolangWeb开发-好用的HTTP客户端httplib(beego)
  • 对智能手表进行逆向工程
  • 数据结构:二叉搜索树详解
  • 搭建SSL邮件服务器
  • 2024年最新外包干了10个月,技术退步明显,程序人生
  • 基于云效 Windows 构建环境和 Nuget 制品仓库进行 .Net 应用开发