枚举算法(竞赛必备)

一、引言

提到枚举,大家的第一印象可能都是Java亦或者是C++中的enum,如下:

enum 枚举类型名 {
    常量1,
    常量2,
    ...
    常量n
};

但在算法竞赛中的 “枚举”,可不止这么简单。重点:算法中的枚举,是一种思想,而不是一种语法结构!( •̀ ω •́ )y

二、枚举算法基础

核心思想:将所有可能罗列出来,逐一检验每个解,是否符合条件。(又称暴力解法-BF解法)

定义:简单来说,就是把所有可能的解空间进行遍历,逐一检验每个可能解是否是问题的真正解。

三、练习

枚举题型,更多会在填空题中考察。

提示( •̀ ω •́ )✧

至少要独立思考10min,若20min后仍无思路在看解析,收获会更大!

1、好数(蓝桥真题)(解析)

2、 (解析)

3、兴趣小组(蓝桥真题)(解析

4、单词分析(蓝桥真题)(解析)

5、约数个数(蓝桥真题)(解析)

6、相乘(蓝桥真题)(解析)

7、直线(蓝桥真题)(解析)

8、大衣最多1的个数(解析)

拓展题型:

1、不高兴的津津(蓝桥真题)(解析)

2、特别数的和(蓝桥真题)(解析)

3、纯质数(蓝桥真题)(解析)

4、k倍区间(蓝桥真题)(解析)


目录

一、入门:

1、好数(蓝桥真题)

 2、蓝

3、兴趣小组(蓝桥真题)

二、基础:

4、单词分析(蓝桥真题)

 5、约数个数(蓝桥真题)

6、相乘(蓝桥真题)

三、进阶:

7、直线(蓝桥真题)

8、大衣最多1的个数


一、入门:

1、好数(蓝桥真题)

(枚举思想,将所有可能枚举出来)

问题描述

一个整数如果按从低位到高位的顺序,奇数位 (个位、百位、万位 ⋯⋯ ) 上的数字是奇数,偶数位 (十位、千位、十万位 ⋯⋯ ) 上的数字是偶数,我们就称之为 “好数”。

给定一个正整数 NN,请计算从 1 到 NN 一共有多少个好数。

输入格式

一个整数 NN。

输出格式

一个整数代表答案。

样例输入 

24

样例输出 

7

样例说明

对于第一个样例,2424 以内的好数有 11、33、55、77、99、2121、2323,一共 77 个。

评测用例规模与约定

对于 10%10% 的评测用例,1≤N≤1001≤N≤100 。

对于 100%100% 的评测用例,1≤N≤1071≤N≤107 。

Java版:

import java.util.Scanner; // 导入Scanner类,用于从控制台读取输入

public class Main {

    // 定义一个方法来判断一个数字是否是“好数字”
    public static boolean isGoodNum(int num) {
        boolean flag = true; // 初始化标志位为true,假设当前数字是“好数字”
        String str = Integer.toString(num); // 将整数num转换为字符串形式,便于逐位处理
        // 从字符串的最后一位开始向前遍历
        for (int i = str.length() - 1; i >= 0; --i) {
            int number = str.charAt(i) - '0'; // 获取该位置的数字(字符转整数)
            int place = str.length() - i; // 计算该数字在原数字中的位置(从1开始计数)
            if (place % 2 == 0) { // 如果该位置是偶数位
                if (number % 2 != 0) { // 如果该位置上的数字是奇数
                    flag = false; // 标记为非“好数字”
                    break; // 结束循环
                }
            } else { // 如果该位置是奇数位
                if (number % 2 == 0) { // 如果该位置上的数字是偶数
                    flag = false; // 标记为非“好数字”
                    break; // 结束循环
                }
            }
        }
        return flag; // 返回标志位,表示是否是“好数字”
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in); // 创建一个Scanner对象,用于读取输入
        int N = scanner.nextInt(); // 读取用户输入的整数N
        int goodNumCount = 0; // 初始化计数器,记录“好数字”的数量
        // 遍历从1到N的所有整数
        for (int i = 1; i <= N; ++i) {
            if (isGoodNum(i)) { // 调用isGoodNum方法检查当前数字是否为“好数字”
                goodNumCount++; // 如果是,则计数器加1
            }
        }
        System.out.println(goodNumCount); // 输出“好数字”的总数
        scanner.close(); // 关闭scanner对象,防止资源泄露
    }
}

C++版:

#include <iostream>
using namespace std;

bool is_GoodNum(int num){
    bool flag = true;
    string str = to_string(num);
    for(int i=str.size()-1; i>=0; --i){
        int number = str[i]-'0'; // 获取该位置的数字
        int place = str.size()-i; // 用数组代表,个位、十位、百位...
        if(place%2==0){ // 偶数位
            if(number%2!=0){
                flag = false;
                break;
            }
        } else { // 奇数位
            if(number%2==0){ // 奇数位若是偶数,则错误
                flag = false;
                break;
            }
        }
    }
    return flag;
}

int main()
{
    int N;
    cin>>N;
    int GoodNum = 0;
    for(int i=1; i<=N; ++i){
        if(is_GoodNum(i)) GoodNum++;
    }
    cout<<GoodNum<<endl;
    return 0;
}

 2、

本题有两点需注意,

1、类型大小要开到long(C++是 long long),int只能表示(

-2,147,483,648 - 2,147,483,647之间的数字,大致就是:1的9次方左右,两亿。

超出范围,就会报错,就需要用long来储存。

且C++版中 num += (long long)vec[i] * vec[j] * vec[k]; 

与 num += (long long)(vec[i] * vec[j] * vec[k];)  是不一样的。

第二者,是错的

2、本题可用递归解决,但介于在练习枚举,便无伤大雅。

(额外,C++的switch要牢记语法框架)

问题描述

小蓝打算去参加蓝桥杯,他看到 lanqiao 这个英文,于是想到了一个问题。

现在有 nn 个小写英文单词,每个单词长度 1≤∣Si∣≤101≤∣Si​∣≤10,他想从这些字符串中选出 33 个单词,要求这三个单词的首字符分别是 lqiao 55 个字母中的一个,且首字母不能有重复。

现在小蓝想知道有多少种选择单词的方案,你可以写一个程序帮助他吗?

输入格式

第一行输入一个 nn ,表示输入的字符串的数量。

第二行输入 nn 个由空格隔开的小写英文单词 SiSi​。

输出格式

输出一个整数,表示小蓝可以选择单词的方案数。

样例输入

5
lan qiao bei operation qqq

样例输出

2

说明

我们可以选择:lanqiaooperation 与 lanqqqoperation

但是我们不能选择:qqqqiaolan,因为 q 字母重复了。

Java版:

import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        //在此输入您的代码...
        long n=scan.nextLong();
        long[] arr={0,0,0,0,0};
        while(n-- > 0){
          String str=scan.next();
          if(str.charAt(0)=='l')  arr[0]++;
          else if(str.charAt(0)=='q') arr[1]++;
          else if(str.charAt(0)=='i') arr[2]++;
          else if(str.charAt(0)=='a') arr[3]++;
          else if(str.charAt(0)=='o') arr[4]++;
        }
        // 初始化一个长整型变量 sum,用于存储从以 'l'、'q'、'i'、'a'、'o' 开头的单词中选取 3 种不同开头的单词组合的总数
        long sum = 0;

        // 使用三层嵌套的 for 循环来枚举所有可能的 3 种不同开头的单词组合
        // 外层循环控制第一个索引 i,范围是从 0 到 2(因为要保证后续有两个不同的索引)
        for (int i = 0; i < 3; i++) {
            // 中层循环控制第二个索引 j,范围是从 i + 1 到 3,确保 j 大于 i
            for (int j = i + 1; j < 4; j++) {
                // 内层循环控制第三个索引 k,范围是从 j + 1 到 4,确保 k 大于 j
                for (int k = j + 1; k < 5; k++) {
                    // 计算当前这 3 种开头的单词组合的数量,通过将对应数组元素相乘得到
                    // 并将结果累加到 sum 中
                    sum += arr[i] * arr[j] * arr[k];
                }
            }
        }

        // 输出最终计算得到的组合总数
        System.out.println(sum);

        // 关闭 Scanner 对象,释放相关资源,避免资源泄漏
        scan.close();
    }
}

C++版:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    // 本题是枚举类型,所以只需要记录每个字母开头,有几个单词就够了
    int t;
    cin >> t;
    string str;
    vector<int> vec(5, 0); // 标记每种单词依次+1
    while (t--)
    {
        cin >> str;
        // 根据单词的首字母,更新对应的计数
        switch (str[0])
        {
        case 'l':
            vec[0]++;
            break;
        case 'q':
            vec[1]++;
            break;
        case 'i':
            vec[2]++;
            break;
        case 'a':
            vec[3]++;
            break;
        case 'o':
            vec[4]++;
            break;
        default:
            // 若输入的单词不是以 'l', 'q', 'i', 'a', 'o' 开头,则跳过该单词
            continue;
        }
    }
    long long num = 0;
    // 用3层循环,把所有可能枚举出来
    // 其实还能用递归代替,但是现在是在讲解简单枚举,所以不用复杂方法
    for (int i = 0; i < 3; ++i)
    {
        for (int j = i + 1; j < 4; ++j)
        {
            for (int k = j + 1; k < 5; ++k)
            {
                num += (long long)vec[i] * vec[j] * vec[k];
            }
        }
    }
    cout << num << endl;
    return 0;
}

3、兴趣小组(蓝桥真题)

Java版:

public class Main {
    // 定义一个公共类 Main,Java 程序执行时需要一个包含 main 方法的公共类作为入口
    public static void main(String[] args) {
        // 定义一个整型数组 resA,用于存储一组整数数据
        // 这里只是示例,实际使用时省略号 ... 应替换为完整的数据
        int[] resA = {12894792, 92774113, 59529208, 22962224 ... };
        // 定义一个整型数组 resB,同样用于存储一组整数数据
        // 省略号 ... 需替换为完整的数据
        int[] resB = {44894050, 34662733, 44141729, 92774113 ... };
        // 定义一个整型数组 resC,存储另一组整数数据
        // 省略号 ... 要替换为完整的数据
        int[] resC = {13404901, 39952424, 47847739, 94939581 ... };

        // 定义一个整型变量 num,初始化为 0,用于统计满足特定条件的元素个数
        int num = 0;

        // 使用增强 for 循环遍历 resA 数组
        // 每次循环,变量 i 依次取 resA 数组中的每个元素
        for (int i : resA) {
            // 定义一个布尔型变量 flag,初始化为 false
            // 该变量用于标记当前元素 i 是否满足存在于 resB 且不存在于 resC 的条件
            boolean flag = false;

            // 使用增强 for 循环遍历 resB 数组
            // 每次循环,变量 j 依次取 resB 数组中的每个元素
            for (int j : resB) {
                // 比较当前 resA 中的元素 i 和 resB 中的元素 j 是否相等
                if (i == j) {
                    // 如果相等,将 flag 置为 true,表示 i 在 resB 中被找到了
                    flag = true;
                }

                // 使用增强 for 循环遍历 resC 数组
                // 每次循环,变量 k 依次取 resC 数组中的每个元素
                for (int k : resC) {
                    // 比较当前 resA 中的元素 i 和 resC 中的元素 k 是否相等
                    if (i == k) {
                        // 如果相等,将 flag 置为 false,表示 i 存在于 resC 中,不满足条件
                        flag = false;
                    }
                }
            }

            // 判断 flag 的值
            if (flag) {
                // 如果 flag 为 true,说明元素 i 存在于 resB 且不存在于 resC
                // 满足条件,将计数器 num 加 1
                num++;
            }
        }

        // 使用 System.out.println 方法输出满足条件的元素个数 num
        System.out.println(num);
    }
}

C++版:

#include <iostream>
#include <vector>
using namespace std;
int main()
{
  vector<int> resA{ 12894792, 92774113, 59529208, 22962224 ... };
  vector<int> resB{ 44894050, 34662733, 44141729, 92774113 ... };
  vector<int> resC{ 13404901, 39952424, 47847739, 94939581 ... };
  int num = 0;
 for(int i : resA){
  bool flag = false;
    for(int j : resB){
      if(i==j) flag=true;
      for(int k : resC){
        if(i==k) flag=false;
      }
    }
    if(flag) num++;
}
cout<<num<<endl;
  return 0;
}
二、基础:

4、单词分析(蓝桥真题)

Java版:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // 创建一个大小为 26 的整型数组,用于统计每个字母的出现次数,初始值都为 0
        int[] arr = new int[26];
        // 创建一个 Scanner 对象,用于从标准输入读取用户输入的字符串
        Scanner scanner = new Scanner(System.in);
        // 读取用户输入的一行字符串
        String str = scanner.nextLine();
        // 关闭 Scanner 对象,释放资源
        scanner.close();

        // 遍历字符串中的每个字符
        for (char c : str.toCharArray()) {
            // 计算当前字符相对于 'a' 的偏移量,作为数组的索引
            int num = c - 'a';
            // 对应索引位置的计数加 1
            arr[num]++;
        }

        // 初始化字母为 'a'
        char letter = 'a';
        // 初始化最大出现次数为 -1
        int letterNum = -1;

        // 遍历数组,找出出现次数最多的字母
        for (int i = 0; i < 26; i++) {
            // 如果当前字母的出现次数大于之前记录的最大次数
            if (letterNum < arr[i]) {
                // 更新最大出现次数
                letterNum = arr[i];
                // 根据索引计算对应的字母
                letter = (char) (i + 'a');
            }
        }

        // 输出出现次数最多的字母
        System.out.println(letter);
        // 输出该字母的出现次数
        System.out.println(letterNum);
    }
}

C++版:

#include <iostream>
using namespace std;
int main()
{
    // 谈论到字典,能用26个英语字母表示
    // 也能用map表示
    int arr[26]{0};
    string str;
    cin>>str;
    for(char c : str){
        int num = c-'a';
        arr[num]++;
    }
    char letter = 'a';
    int letterNum = -1;;
    for(int i=0; i<26; ++i){
        if(letterNum<arr[i]){
            letterNum = arr[i];
            letter = i+'a';
        }
    }
    cout<<letter<<endl;
    cout<<letterNum<<endl;
    return 0;
}

 5、约数个数(蓝桥真题)

首先了解一下:什么是约数 # 小学数学

// 这道题目难就难在你不知道,啥是约数!然后没了
// 其他就是枚举(遍历所有可能),判断一下。

Java版:

public class Main {
    public static void main(String[] args) {
        // 初始化变量number,用于记录1200000的约数个数,初始值为0
        int number = 0;
        // 使用for循环进行枚举,从1到1200000
        for (int i = 1; i <= 1200000; ++i) {
            // 判断i是否为1200000的约数,如果1200000除以i的余数为0,则i是1200000的约数
            if (1200000 % i == 0) {
                // 如果i是约数,将number的值加1
                number++;
            }
        }
        // 输出1200000的约数个数
        System.out.println(number);
    }
}

C++版:

#include <iostream>
using namespace std;

// 这道题目难就难在你不知道,啥是约数!然后没了
// 其他就是枚举(遍历所有可能),判断一下。

int main()
{
  int number = 0;
  for(int i=1; i<=1200000; ++i){
    if(1200000%i==0) number++;
  }
  cout<<number<<endl;
  return 0;
}

6、相乘(蓝桥真题)

其实本题超级超级简单,这种题型也说明了为啥蓝桥杯,被称为暴力杯。

C++做这道题时不用担心超时。但是Java一定!要在循环结束后➕break,否则必然超时!

Java版:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // 定义一个长整型变量 flag,用于存储最终符合条件的 i 值,初始化为 0
        long flag = 0;

        // 开始循环,从 1 到 1000000007 进行遍历
        for (long i = 1; i <= 1000000007; i++) {
            // 计算 i 乘以 2021 之后对 1000000007 取模的结果,存储在 num 中
            long num = (i * 2021) % 1000000007;

            // 判断 num 对 1000000007 取模的结果是否等于 999999999
            if (num % 1000000007 == 999999999) {
                // 如果满足条件,将当前的 i 值赋给 flag
                flag = i;
                break;
            }
        }

        // 输出最终找到的符合条件的 i 值
        System.out.println(flag);
    }
}

C++版:

#include <iostream>
#define ll long long
using namespace std;
int main()
{
  ll flag = 0;
  for(ll i=1; i<=1000000007; ++i){
    ll num = i*2021%1000000007;
    if(num%1000000007 == 999999999) flag = i;
  }
  cout<<flag<<endl;
  return 0;
}

三、进阶:

其实说是进阶,更不如说是综合题目,将多种知识糅合在一起,形成题目。

7、直线(蓝桥真题)

Java不懂HashSet的点击这里

这道题目就是典型的,嘴上说着超级超级难,其实超级简单!

因为他就是一道(枚举+数学题型)

为啥呢?看完这个公式就知道了:y=mx+C

另大家感到难受无非两个点

1、怎么把所有直线表示出来。

2、怎么判断表示出来的直线,是否重复!

第一条,挺好解决的,就是个枚举问题,答案大家肯定能看懂。

第二条,更容易解决,判断他的斜率是否相等就行。

m相等,证明两条直线平行或重合。C相等,证明两条直线相交。

题目描述

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上, 那么这些点中任意两点确定的直线是同一条。

给定平面上 2 × 3 个整点(x,y)∣0≤x<2,0≤y<3,x∈Z,y∈Z(x,y)∣0≤x<2,0≤y<3,x∈Z,y∈Z​,即横坐标 是 00 到 11 (包含 0 和 1) 之间的整数、纵坐标是 0 到 2 (包含 0 和 2) 之间的整数 的点。这些点一共确定了 11 条不同的直线。

给定平面上 20×2120×21 个整点 (x,y)∣0≤x<20,0≤y<21,x∈Z,y∈Z(x,y)∣0≤x<20,0≤y<21,x∈Z,y∈Z,即横 坐标是 00 到 1919 (包含 00 和 1919) 之间的整数、纵坐标是 00 到 2020 (包含 00 和 2020​) 之 间的整数的点。

请问这些点一共确定了多少条不同的直线。

Java题解

import java.util.HashSet;
import java.util.Set;

public class Main {
    /*
      0<=x<20
      0<=y<21
      这些点确定了多少直线
      
      提示:
      0<=x<2
      0<=y<3
      这些点确定了11条直线
      
      对于 y=ax+b, 
     */
    public static void main(String[] args) {
        Set<String> set= new HashSet<>();
        //遍历x1,x2,y1,y2四个点
        for (int x1 = 0; x1 < 20; x1++) {
            for (int y1= 0; y1 <21; y1++) {
                for(int x2 =0;x2<20;x2++) {
                    for (int y2= 0; y2 <21; y2++) {
                        set.add(fun(x1,y1,x2,y2));
                    }
                }
            }
        }
        System.out.println(set.size()-1); //去掉下面第一种情况
    }

    private static String fun(int x1, int y1, int x2, int y2) {
        //虽然set支持去重(方程一样),但是对于下面的三种情况,无法去重。
        if(x1==x2&&y1==y2) {
            return "_";
        }
        else if(x1==x2) {
            return "x="+x1; // 垂直于 x 轴
        }
        else if(y1==y2) {
            return "y="+y1;// 垂直于 y 轴
        }else {
            double k  =(y2-y1)*1.0 /(x2-x1);
            double b = (y1*x2 - y2*x1)*1.0/(x2-x1);
            b+=0.0; //注意,这里必须加这一步,因为上一步的计算并不会改变b变量本身的数据类型
            return "y="+k+"x+"+b; //交给set存储
        }
    }
}

C++题解 

#include <iostream>
#include <set>
using namespace std;

// 在做这种题目时,为避免精度问题,尽量将公式展开
// 接下来,两行其实是等价的,都是由一个公式推导出来的,但是第一个是错的!
// double c = (double)(y0-k*x0);
// double c = (double)(x0*y1-x1*y0)/(x0-x1);
// 因为,第一个,减少了中间步骤,也就减少了浮点数精度误差累积的机会,所以在某些情况下可能会得到更准确的结果。

int main()
{
    set<pair<double,double>> s1;
    for(int x0=0; x0<20; ++x0){
        for(int x1=0; x1<20; ++x1){
            for(int y0=0; y0<21; ++y0){
                for(int y1=0; y1<21; ++y1){
                    if(x1==x0&&y1==y0) continue; // 两点相同的情况
                    if(x1==x0) continue; // 垂直时,跳过,后面加上20
                    // 分析垂直情况
                    double k = (double)(y0-y1)/(x0-x1);
                    //  double c = (double)(y0-k*x0);
                    double c = (double)(x0*y1-x1*y0)/(x0-x1);
                    s1.insert({k,c});
                }
            }
        }
    }

    cout<<s1.size()+20<<endl;

    return 0;
}

8、大衣最多1的个数

不了解⊕符号的,请点击下方!!!

了解符号异或( ^ 也就是本题中的  ⊕)

问题描述

大衣有一个长度为 NN 的二进制字符串 SS。

你需要构造一个长度为 NN 的字符串 XX:

  • 对于每一个索引 i(1≤i≤N)i(1≤i≤N),有 X(i+1)=Xi⊕SiX(i+1)​=Xi​⊕Si​,其中 ⊕⊕ 表示按位异或。
  • 字符串 X​X​ 中字符 1​1​ 的个数最多。

请找出字符串 XX 中 11 的最多个数。

输入格式

第一行输入一个正整数 T​T​ 表示测试数据的组数。

接下来 TT 组测试数据每组输入两行:

  • 第一行输入一个正整数 NN 表示字符串 SS 的长度。

  • 第二行输入一个长度为 NN 个二进制字符串 SS。

输出格式

对于每组测试数据,输出一个整数表示字符串 XX 中 1​1​ 的最多个数,并换行。

样例输入1

3
4
0011
4
1100
4
1010

样例输出1

3
3
2

说明

样例 11:考虑字符串 X=1110X=1110,其中 X2=X1⊕S1=1⊕0,X3=X2⊕S2=1⊕0,X4=X3⊕S3=1⊕1X2​=X1​⊕S1​=1⊕0,X3​=X2​⊕S2​=1⊕0,X4​=X3​⊕S3​=1⊕1,字符串包含 33 个 11,可以证明没有更多的构造方案。

样例 2​2​:考虑字符串 X=1011​X=1011​,其中 X2=X1⊕S1=1⊕1,X3=X2⊕S2=0⊕1,X4=X3⊕S3=1⊕0​X2​=X1​⊕S1​=1⊕1,X3​=X2​⊕S2​=0⊕1,X4​=X3​⊕S3​=1⊕0​,字符串包含 3​3​ 个 1​1​,可以证明没有更多的构造方案。

样例 33:考虑字符串 X=0110X=0110,字符串包含 22 个 11,可以证明没有更多的构造方案。

java版

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // 读取测试用例的数量
        int t = scanner.nextInt();
        while (t > 0) {
            // 读取字符串的长度
            int N = scanner.nextInt();
            // 读取字符串
            String str = scanner.next();
            // 用于记录初始值为 0 和 1 时 1 的个数
            int num0 = 0;
            int num1 = 0;
            // 初始化 X0 为 0,X1 为 1
            int X0 = 0;
            int X1 = 1;
            // 遍历字符串
            for (int i = 0; i < N; i++) {
                // 将字符转换为整数
                int S = str.charAt(i) - '0';
                // 假设 X 初始为 0
                if (X0 == 1) {
                    num0++;
                }
                // 假设 X 初始化为 1
                if (X1 == 1) {
                    num1++;
                }
                // 更新 X0 和 X1 的值
                X0 = X0 ^ S;
                X1 = X1 ^ S;
            }
            // 输出 num0 和 num1 中的最大值
            System.out.println(Math.max(num0, num1));
            t--;
        }
        scanner.close();
    }
}

C++版

#include <iostream>
using namespace std;
// 其实不难,别被题目|゚Д゚)))吓到
// 本质就俩,
// 1、理解异或运算符
// 2、知道这是一个递推公式,只需要把X0初始化1或0就行。
int main()
{
    // 这道题目很简单,只是会害怕与字符串
    int t;
    cin>>t;
    while(t--){
        int N;
        cin>>N;
        string str = ""; // 初始化
        cin>>str; // 输入字符串
        int num0 = 0; // 用于记录1的个数
        int num1 = 0;
        int X0 = 0;
        int X1 = 1;
        for(int i=0; i<N; ++i){ // 必须要完整的遍历,最后一次遍历X0,X1也会改变,但是不会生效。
            int S = str[i]-'0';
            // 假设X初始为0
            if(X0==1) num0++;
            // 假设X初始化为1
            if(X1==1) num1++;
            X0 = X0^S;
            X1 = X1^S;
        }
        cout<<max(num0,num1)<<endl;
    }
    return 0;
}
四、拓展:

(由学习部强烈推荐)

1、不高兴的津津(蓝桥真题)

(由学习部推荐,并挑选答案)

题目描述

津津上初中了。妈妈认为津津应该更加用功学习,所以津津除了上学之外,还要参加妈妈为她报名的各科复习班。另外每周妈妈还会送她去学习朗诵、舞蹈和钢琴。但是津津如果一天上课超过八个小时就会不高兴,而且上得越久就会越不高兴。假设津津不会因为其它事不高兴,并且她的不高兴不会持续到第二天。请你帮忙检查一下津津下周的日程安排,看看下周她会不会不高兴;如果会的话,哪天最不高兴。

输入描述

输入七行数据,分别表示周一到周日的日程安排。每行包括两个小于 1010 的非负整数,用空格隔开,分别表示津津在学校上课的时间和妈妈安排她上课的时间。

输出描述

输出一个数字。如果不会不高兴则输出 00,如果会则输出最不高兴的是周几(用 1,2,3,4,5,6,71,2,3,4,5,6,7 分别表示周一,周二,周三,周四,周五,周六,周日)。如果有两天或两天以上不高兴的程度相当,则输出时间最靠前的一天。

输入输出样例

示例 1

输入

5 3
6 2
7 2
5 3
5 4
0 4
0 6

输出

3
Java版
import java.util.Scanner;

public class 不高兴的津津 {
    public static void main(String[] args) {
        Scanner scan=new Scanner(System.in);
        //创建3个数字arr1表示上课,arr2表示补课,sum表示一天心情
        int[]arr1=new int[7];
        int[]arr2=new int[7];
        int[]sum=new int[7];
        for (int i = 0; i <7; i++) {
            arr1[i]= scan.nextInt();
            arr2[i]= scan.nextInt();
            sum[i]=arr1[i]+arr2[i];
        }
        int max=0;
        int n=0;
        //找出最大值,并用n来标记
        for (int i = 0; i <7; i++) {
            if (sum[i]>max){
                max=sum[i];
                n=i;
            }
        }
        //判断最大值是否满足开不开心的条件
        if (max<=8){
            System.out.println(0);
        }else System.out.println(n+1);
    }
}
2、特别数的和(蓝桥真题)

(由学习部推荐,并挑选答案)

题目描述

小明对数位中含有 2、0、1、9 的数字很感兴趣(不包括前导 0),在 1 到 40 中这样的数包括 1、2、9、10 至 32、39 和 40,共 28 个,他们的和是 574。

请问,在 1 到 nn 中,所有这样的数的和是多少?

输入描述

输入格式:

输入一行包含两个整数 n(1≤n≤104)n(1≤n≤104)。

输出描述

输出一行,包含一个整数,表示满足条件的数的和。

输入输出样例

示例

输入

40

输出

574
Java版
import java.util.Scanner;

public class 特别数的和 {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        //sum要用long类型
        long sum = 0;
        for (int i = 1; i <= n; i++) {
            //将每个数都转换为字符串类型,再用contains方法进行检测
            String s = String.valueOf(i);
            if (s.contains("2") || s.contains("0") || s.contains("1") || s.contains("9")) {
                //检测到之后转换为long类型,进行求和
                long l = Long.parseLong(s);
                sum += l;
            }
        }
        System.out.println(sum);
    }
}
3、纯质数(蓝桥真题)

(由学习部推荐,并挑选答案)

题目描述

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

如果一个正整数只有 11 和它本身两个约数,则称为一个质数(又称素数)。

前几个质数是:2,3,5,7,11,13,17,19,23,29,31,37,⋅⋅⋅2,3,5,7,11,13,17,19,23,29,31,37,⋅⋅⋅ 。

如果一个质数的所有十进制数位都是质数,我们称它为纯质数。例如:2,3,5,7,23,372,3,5,7,23,37 都是纯质数,而 11,13,17,19,29,3111,13,17,19,29,31 不是纯质数。当然 1,4,351,4,35 也不是纯质数。

请问,在 11 到 2021060520210605 中,有多少个纯质数?

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M
Java版
import java.util.Scanner;

public class 纯质数 {
    public static void main(String[] args) {
        Scanner scan=new Scanner(System.in);
        long sum=0;
        boolean b=false;
        for (int i = 2; i <=20210605; i++) {
            boolean q=false;
            //检验是否为质数
            for (int j = 2; j <=Math.sqrt(i); j++) {
                if (i%j==0&&i!=j){
                    q=true;
                    break;
                }
            }
            //如果不是质数会直接进入下一次循环
            if (q){
                continue;
            }
            //如果这个数为质数,开始对每一位进行质数检测
            String s=String.valueOf(i);
            for (int j = 0; j <s.length(); j++) {
                int a=s.charAt(j)-48;
                b=false;
                if (a==0||a==1){b=true;}
                //进行质数检测
                for (int k = 2; k <=Math.sqrt(a); k++) {
                    if (a%k==0&&a!=k){
                        b=true;
                        break;
                    }
                }
                if (b){
                    break;
                }
            }//当所有位置全部都为质数,进行++,有一个位置不通过b就会变成false
            if (!b) {
                sum++;
            }
        }
        System.out.println(sum);
    }
}
4、k倍区间(蓝桥真题)

(由学习部推荐,并挑选答案)

题目描述

给定一个长度为 NN 的数列,A1,A2,⋯ANA1​,A2​,⋯AN​,如果其中一段连续的子序列 Ai,Ai+1,⋯AjAi​,Ai​+1,⋯Aj​ ( i≤ji≤j ) 之和是 KK 的倍数,我们就称这个区间 [i,j][i,j] 是 K 倍区间。

你能求出数列中总共有多少个 KK 倍区间吗?

输入描述

第一行包含两个整数 NN 和 KK( 1≤N,K≤1051≤N,K≤105 )。

以下 N 行每行包含一个整数 AiAi​ ( 1≤Ai≤1051≤Ai​≤105 )

输出描述

输出一个整数,代表 K 倍区间的数目。

输入输出样例

示例

输入

5 2
1
2
3
4
5

输出

6
Java版
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改

public class k倍区间 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        long sum = 0;
        long[] mod = new long[k];
        for(int i=0;i<n;i++) {
            sum += sc.nextLong();
// 利用求余数 将相同余数的元素放到一起
            mod[(int)(sum%k)]++;
        }
//  余数为0的元素自成一个 k倍区间
        long res = mod[0];
        for(int i=0;i<k;i++) {
// 两个相同余数的元素。他们的差一定为 k倍区间, 13%2=1 25%2=1  (25-13)%2==0
// 任意两个数都可以组合一次 相当于 c(2,x) =  x(x-1)/2
            res += (mod[i]*(mod[i]-1))/2;
        }
        System.out.println(res);
    }
}

(切记,自己先实践,仍然无头绪,在询问组长)


借鉴博客、视频、网站

1、蓝桥杯官网

2、什么是约数 # 小学数学

3、Java不懂HashSet的点击这里

4、了解符号异或( ^ 也就是本题中的  ⊕)


( •̀ ω •́ )✧点击这里,继续学习其他模块吧!

评论 57
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值