备战蓝桥杯,用JAVA刷洛谷算法题单:【算法1-3】暴力枚举

本文介绍了在洛谷编程题集中,涉及的几种基于暴力枚举的算法问题,包括统计方形、子集选择、全排列以及优化后的火星人问题。通过循环和递归实现,展示了如何通过剪枝和优化提升解题效率。
摘要由CSDN通过智能技术生成

参考

【算法1-3】暴力枚举 - 题单 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

image-20240304103149822

循环枚举

P2241 统计方形(数据加强版)

只能过40%?

对于一个n*m的格子,如果在n边放边长为1的小方格,能放n个,以此类推t边放i长的小方格能放(t-i+1)个,那么对于i*j的矩形,在n*m中总共可以放n边能放的个数*m边能放的个数。

其中i=j时便是正方形,反之为长方形,对i和j做循环,对总数做累加即可。

package _1_3.xun_huan_mei_ju;

import java.util.Scanner;

public class P2241 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();

        // 行/列中边长为i(1、2……t)的可能位置个数=t、t-1……1,即(t-i+1)

        // lenN记录在n边中,边长为i的位置个数
        // lenM记录在m边中,边长为i的位置个数
        int[] lenN = new int[n + 1];
        int[] lenM = new int[m + 1];
        for (int i = 1; i <= n; i++) {
            lenN[i] = n - i + 1;
        }
        for (int i = 1; i <= m; i++) {
            lenM[i] = m - i + 1;
        }

        // 对于正方形,sum1等于 宽1*长1       +   宽2*长2       此类的累加
        // 对于长方形,sum2等于 宽1*长其他值   +   宽2*长其他值   此类的累加
        // ij分别表示两个边长

        int sum1 = 0;
        int sum2 = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (i != j) {
                    sum2 += lenN[i] * lenM[j];
                } else {
                    sum1 += lenN[i] * lenM[j];
                }
            }
        }

        System.out.printf("%d %d", sum1, sum2);
        scanner.close();
    }
}

子集枚举

P1036 [NOIP2002 普及组] 选数

dfs搜索,结束条件是size==k。

去重:不降原则,记录index从而使得循环从index后面开始

package _1_3.zi_ji_mei_ju;

import java.util.Arrays;
import java.util.Scanner;

public class P1036 {

    private static int k;
    private static int n;
    private static int[] nums;
    private static int count;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String[] s1 = scanner.nextLine().split(" ");
        int[] ints = Arrays.stream(s1).mapToInt(Integer::valueOf).toArray();
        n = ints[0];
        k = ints[1];
        count = 0;
        String[] s2 = scanner.nextLine().split(" ");
        nums = Arrays.stream(s2).mapToInt(Integer::valueOf).toArray();
        // 注意size从0开始,加上k个数后size=k-1,再search(k)进行判断
        search(0, 0, 0);
        System.out.println(count);
        scanner.close();
    }

    public static void search(int size, int sum, int index) {
        if (size == k) {
            if (isPrim(sum)) count++;
            return;
        }
        for (int i = index; i < n; i++) {
            sum += nums[i];
            search(size + 1, sum, i + 1);
            sum -= nums[i];
        }
    }

    public static boolean isPrim(int n) {
        if (n <= 1) {
            return false;
        }
        for (int i = 2; i < n; i++) {
            if (n % i == 0) {
                return false;
            }
        }
        return true;
    }
}

排列枚举

P1088 [NOIP2004 普及组] 火星人

方法一:20%,暴力枚举,全排列找到start,然后再找m个就是结果

改进:在找到start这个部分剪枝,参考 洛谷刷题之P1088 [NOIP2004普及组] 火星人 - 知乎 (zhihu.com)

package _1_3.pai_lie_mei_ju;

import java.util.Scanner;

public class P1088 {

    private static String start;
    private static int n;
    private static int add;
    private static boolean flag;
    private static int count;
    private static boolean toContinue;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        n = Integer.parseInt(scanner.nextLine());
        add = Integer.parseInt(scanner.nextLine());
        start = scanner.nextLine().replaceAll(" ", "");


        toContinue = true;
        flag = false;
        // count必须为全局,否则会跟随dfs变化
        count = 0;
        dfs(new StringBuilder());
        scanner.close();
    }

    // 一直全排列到符合start,然后再进行add次全排列即可得到最终结果
    public static void dfs(StringBuilder now) {
        if (!toContinue) {
            return;
        }
        if (count == add) {
            for (char c : now.toString().toCharArray()) {
                System.out.printf("%c ", c);
            }
            toContinue = false;
            return;
        }
        for (int i = 1; i <= n; i++) {
            if (now.indexOf(i + "") == -1) {
                now.append(i);
                // 一直搜索到start,此期间count不变
                if (!flag) {
                    // algo List<String>->String
                    // 1 String.join("-",list)
                    // 2 list.stream().collect(Collectors.joining()).toString();
                    // 3 StringBuilder
                    if (now.toString().equals(start)) {
                        flag = true;
                    }
                    dfs(now);
                } else {
                    // 已经从start开始搜索
                    if (now.length() == n) {
                        // 找到start之后一个符合长度的顺序
                        count++;
                        dfs(now);
                    } else dfs(now);
                }
                now.deleteCharAt(now.length() - 1);
            }
        }
    }
}

方法二:字段序算法获取当前序列的下一个序列,参考

当前序列的下一个序列(字典序算法)Java实现_java中如何插入序列的下一个值-CSDN博客

字典序算法_字典排序法算法思路-CSDN博客

package _1_3.pai_lie_mei_ju;

import java.util.Scanner;

public class P1088_2 {

    private static int[] start;
    private static int n;
    private static int k;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        n = Integer.parseInt(scanner.nextLine());
        k = Integer.parseInt(scanner.nextLine());
        start = new int[n];
        for (int i = 0; i < n; i++) {
            start[i] = scanner.nextInt();
        }
        for (int i = 0; i < k; i++) {
            next_permutation(start);
        }
        for (int i = 0; i < n; i++) {
            System.out.printf("%d ", start[i]);
        }
        scanner.close();
    }

    public static void next_permutation(int[] arr) {
        // algo 字典序算法
        // 设P是集合{1、2、……n-1、n}的一个全排列:P = p1p2……pj-1pjpj+1……pn(1 ≤ p1、 p2、……、pn ≤ n-1)
        // 第一步:从排列的右端开始,找出第一个比右边数字小的数字的序号j,即j = max { i | pi < pi+1 ,i > j }
        // 第二步:在pj 的右边的数字中,找出所有比pj大的数字中最小的数字pk,即 k = min { i | pi > pj ,i > j}
        // 第三步:交换pi,pk
        // 第四步:再将排列右端的递减部分 [pj+1pj+2……pn]倒转,就得到了一个新的排列 P’ = p1p2…pj-1pkpnpn-1…pk+1pjpk-1…pj+1
        for (int i = n - 1; i > 0; i--) {
            // 1、从后往前找第一对逆序,逆序对前一个下表为t
            if (arr[i - 1] < arr[i]) {
                int t = i - 1;
                // 如果t==n-1,只需要交换最后两个数即可
                if (t == n - 2) {
                    swap(arr, t, n - 1);
                } else {
                    // 2、在t之后的数中,找到比t大且符合条件的最小的数
                    // 注意这里的min是下标,先找到符合比t大的作为min的初始值,否则各种错误(如果设置t+1为初始值,不能保证arr[t+1]比arr[t]大)
                    int min = t;
                    while (arr[min] <= arr[t]) min++;
                    for (int j = t + 1; j < n; j++) {
                        if (arr[j] > arr[t] && arr[j] < arr[min]) {
                            min = j;
                        }
                    }
                    // 3、交换t和min的值
                    swap(arr, t, min);
                    // 4、在t之后的数中,进行正序排序
                    // 可以注意到,t之后的数都是符合arr[i - 1] >= arr[i]
                    // 交换t和min之后形成了一个递减的数列,所以对递减数列进行翻转即可得到正序
                    int left = t + 1, right = n - 1;
                    while (left < right) {
                        swap(arr, left, right);
                        left++;
                        right--;
                    }
                }
                return;
            }
        }
    }

    private static void swap(int[] arr, int a, int b) {
        int t = arr[a];
        arr[a] = arr[b];
        arr[b] = t;
    }
}

               left++;
                        right--;
                    }
                }
                return;
            }
        }
    }

    private static void swap(int[] arr, int a, int b) {
        int t = arr[a];
        arr[a] = arr[b];
        arr[b] = t;
    }
}

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是算法备战蓝桥杯的一些建议和步骤: 1. 熟悉蓝桥杯的考试内容和题型。了解蓝桥杯的考试规则、题目类型和难度分布,可以从蓝桥杯官方网站或者相关的参考资料中获取这些信息。 2. 学习基础知识。蓝桥杯的题目通常涉及到算法和数据结构的基础知识,例如数组、链表、栈、队列、树、图、排序算法、查找算法等。建议先学习这些基础知识,掌握它们的原理和常见的应用场景。 3. 题练习。通过题来提高算法和编程能力是非常重要的。可以选择一些经典的算法题目进行练习,例如ACM/ICPC、LeetCode、牛客网等平台上的题目。题的过程中要注重理解题目的要求,分析问题的解决思路,编写代码实现解决方案,并进行调试和测试。 4. 参加模拟考试。蓝桥杯的模拟考试可以帮助你熟悉考试的流程和题目类型,也可以检验你的学习成果。参加模拟考试后,可以分析自己的得分情况,找出自己的不足之处,并进行针对性的复习和提高。 5. 学习优秀的解题思路和代码。在题的过程中,可以学习一些优秀的解题思路和代码,了解不同的解题方法和技巧。可以通过查阅相关的参考书籍、博客、论坛等获取这些信息。 6. 多做实战训练。除了题,还可以参加一些实战训练,例如参加ACM/ICPC比赛、编程竞赛等。这些实战训练可以提高你的编程能力和解题速度,也可以锻炼你的团队合作和应对压力的能力。 7. 多与他人交流和讨论。与他人交流和讨论可以帮助你更好地理解和掌握算法和编程知识。可以加入一些算法学习群组、论坛或者参加一些线下的学习活动,与其他学习者一起交流和分享经验。 8. 坚持练习和复习。算法题是一个长期的过程,需要坚持不懈地练习和复习。每天保持一定的学习时间,不断积累和提高自己的算法和编程能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值