CCF CSP认证JAVA(四)

201312-1出现次数最多的数

问题描述

给定n个正整数,找出它们中出现次数最多的数。如果这样的数有多个,请输出其中最小的一个。

输入格式

输入的第一行只有一个正整数n(1 ≤ n ≤ 1000),表示数字的个数。
  输入的第二行有n个整数s1, s2, …, sn (1 ≤ si ≤ 10000, 1 ≤ i ≤ n)。相邻的数用空格分隔。

输出格式

输出这n个次数中出现次数最多的数。如果这样的数有多个,输出其中最小的一个。

样例输入

6
10 1 10 20 30 20

样例输出

10

public class Main {
    public static void main(String [] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] nums = new int[n];
        Map<Integer,Integer> map = new HashMap<>();
        for(int i=0;i<n;i++){
            nums[i]=scanner.nextInt();
            if(!map.containsKey(nums[i])){
                map.put(nums[i],1);
            }else {
                map.put(nums[i],map.get(nums[i])+1);
            }
        }
        int maxValue = map.get(nums[0]);
        int res = nums[0];
        Iterator<Map.Entry<Integer, Integer>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<Integer,Integer> e = iterator.next();
            if(maxValue<e.getValue()){
                maxValue=e.getValue();
                res = e.getKey();
            }else if(maxValue==e.getValue()){
                res = Math.min(e.getKey(),res);
            }
        }
        System.out.println(res);
    }
}

发现对于迭代器遍历Map还是不熟悉,再认真总结一遍吧!!!

Iterator迭代器

  • 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。

  • Java中的Iterator功能比较简单,并且只能单向移动:

  • 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。

  • 使用Object next()获得序列中的下一个元素。

  • 使用boolean hasNext()检查序列中是否还有元素。

  • 使用remove()将迭代器新返回的元素删除。

  • 迭代其实可以简单地理解为遍历,是一个标准化遍历各类容器里面的所有对象的方法类

  • 使用方法:

    for(Iterator it = c.iterator(); it.hasNext(); ) {  
      Object o = it.next();  
       //do something  
    }   
     
    //迭代器用于while循环
     Iterator iter = l.iterator();
     while(iter.hasNext()){
         String str = (String) iter.next();
         System.out.println(str);
     }
    

Entry

  • Map中存放的是键值对Key—Value,每一个键值对都存在着一个映射关系,Map中采用Entry内部类来表示一个映射项,映射项包含Key 和Value(通俗来说:一个键值对就是一个Entry。

  • Map.Entry里面包含getKey()和getValue()方法

    Iterator<Map.Entry<Integer, Integer>> it=map.entrySet().iterator();
        while(it.hasNext()) {
            Map.Entry<Integer,Integer> entry=it.next();
            int key=entry.getKey();
            int value=entry.getValue();
            System.out.println(key+" "+value);
        }
    

entrySet

  • entrySet是 java中 键-值 对的集合,Set里面的类型是Map.Entry,一般可以通过map.entrySet()得到。entrySet实现了Set接口,里面存放的是键值对。一个Key对应一个Value。

  • 用来遍历Map。

    Set<Map.Entry<String, String>> entryseSet=map.entrySet();
     
    for (Map.Entry<String, String> entry:entryseSet) {
     
        System.out.println(entry.getKey()+","+entry.getValue());
     
    }
    

keySet

  • keySet是键的集合,Set里面的类型即key的类型

    Set<String> set = map.keySet();
     
    for (String s:set) {
     
        System.out.println(s+","+map.get(s));
     
    }
    
    public static void main(String[] args) {
     
        Map<String, String> map = new HashMap<String, String>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");
      
        //第一种:普遍使用,二次取值
        System.out.println("通过Map.keySet遍历key和value:");
        for (String key : map.keySet()) {
            System.out.println("key= "+ key + " and value= " + map.get(key));
        }
      
        //第二种
        System.out.println("通过Map.entrySet使用iterator遍历key和value:");
        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
        }
      
        //第三种:推荐,尤其是容量大时
        System.out.println("通过Map.entrySet遍历key和value");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
        }
     
        //第四种
        System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
        for (String v : map.values()) {
            System.out.println("value= " + v);
        }
     }
    

201403-1相反数

问题描述

有 N 个非零且各不相同的整数。请你编一个程序求出它们中有多少对相反数(a 和 -a 为一对相反数)。

输入格式

第一行包含一个正整数 N。(1 ≤ N ≤ 500)。
  第二行为 N 个用单个空格隔开的非零整数,每个数的绝对值不超过1000,保证这些整数各不相同。

输出格式

只输出一个整数,即这 N 个数中包含多少对相反数。

样例输入

5
1 2 3 -1 -2

样例输出

2

public class Main {
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int count = 0;
        int[] num1 = new int[n];
        int[] num2 = new int[n];
        for(int i=0;i<n;i++){
            num1[i]=num2[i]=scanner.nextInt();
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if(num1[i]+num2[j]==0){
                    count++;
                }
            }
        }
        System.out.println(count/2);
    }
}

这就是做水题的快乐吧哈哈哈哈!


201409-1相邻数对

问题描述

给定n个不同的整数,问这些数中有多少对整数,它们的值正好相差1。

输入格式

输入的第一行包含一个整数n,表示给定整数的个数。
  第二行包含所给定的n个整数。

输出格式

输出一个整数,表示值正好相差1的数对的个数。

样例输入

6
10 2 6 3 7 8

样例输出

3

样例说明

值正好相差1的数对包括(2, 3), (6, 7), (7, 8)。

评测用例规模与约定

1<=n<=1000,给定的整数为不超过10000的非负整数。

public class Main {
    public static void main(String [] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] nums = new int[n];
        int count = 0;
        for(int i=0;i<n;i++){
            nums[i]=scanner.nextInt();
        }
        for(int i=0;i<n;i++){
            for (int j=0;j<n;j++){
                if(nums[i]-nums[j]==1){
                    count++;
                }
            }
        }
        System.out.println(count);
    }
}

写水题就是一个字:爽!


202009-3点亮数字人生

在这里插入图片描述

这一题是个大工程,但是里面有很多点值得揣摩,从头开始吧!!!

网上JAVA的题解也太太太太太太少了吧!!!!!留下了学渣的眼泪555555

好不容易找到一篇,大佬总结的真不错,点赞!!!

CCF准备日记——202009-3(邻接矩阵+拓扑排序)_dzc_go的博客-CSDN博客

按位运算

基础知识

  1. 原码与补码

    15 的原码: 00000000 00000000 00000000 00001111
    补码: 11111111 11111111 11111111 11110000

    -15的原码:11111111 11111111 11111111 11110001

    负数的原码即为:正数的原码按位取反,再加1。

  2. 位移操作

    • << 左移

      m<<n的含义:把整数m表示的二进制数左移n位,高位移出n位都舍弃,低位补0. (此时将会出现正数变成负数的形式)

    • >> 右移(算术右移 有符号右移

      m>>n的含义:把整数m表示的二进制数右移n位,m为正数,高位全部补0;m为负数,高位全部补1

    • >>> 无符号右移(逻辑右移 无符号右移

      m>>>n:整数m表示的二进制右移n位,不论正负数,高位都补零。

在不大于自身数值类型最大位数的移位时,
m<<n即在数字没有溢出的前提下,对于正数和负数,左移n位都相当于m乘以2的n次方.
m>>n即相当于m除以2的n次方,得到的为整数时,即为结果。如果结果为小数,此时会出现两种情况:

(1)如果m为正数,得到的商会无条件 的舍弃小数位;

(2)如果m为负数,舍弃小数部分,然后把整数部分加+1得到位移后的值。

四个操作符

  1. ~(按位非):对该整数的二进制形式逐位取反。

  2. | (按位或):对两个整数的二进制形式逐位进行逻辑或运算,原理为:

    1|0=1,0|0=0,1|1=1,0|1=1(有1结果就是1)

  3. &(按位与):对两个整数的二进制形式逐位进行逻辑与运算,原理:

    1|0=0,0|0=0,1&1=1;0&1=0(有0结果就是0)

  4. ^(按位异或):对两个整数的二进制形式逐位进行逻辑异或运算,原理:

    11=0,10=1,01=1,00=0(相同为0,不同为1)


拓扑排序

基本介绍

拓扑排序(Topological Order)是指,将一个有向无环图(Directed Acyclic Graph简称DAG)进行排序进而得到一个有序的线性序列。通俗来说,就是来确定事物发生的顺序的!!!(在拓扑排序中,如果存在一条从顶点A到顶点B的路径,那么在排序结果中B出现在A的后面)。

  • 拓扑排序不必是唯一的,任何合理的排序都是可以的;
  • 一个简单的求拓扑排序的算法是先找出任意一个没有入度的顶点,然后显示该顶点,并将它及其边一起从图中删除,然后,我们对图的其余部分同样应用这样的方法处理。
  • 基本思想:
    1. 从有向图中选一个无前驱(入度为零)的顶点输出;
    2. 将此顶点和以它为起点的弧删除;
    3. 重复1和2,知道不存在无前驱的顶点;
    4. 若此时输出的顶点数小于有向图中的顶点数,则说明有向图中存在回龙路,否则输出的顶点的顺序即为一个拓扑序列。

算法步骤

  1. 求所有顶点的入度,可以附设一个存放各顶点入度的数组indegree[];
  2. 把所有入度为0 的顶点入队列或栈;
  3. 当栈或队列不空时
    1. 出栈或出队列顶点为u,输出顶点u;
    2. 顶点u的所有邻接点入度减一,如果有入度为0的顶点,则入栈或入队列
  4. 若此时输出的顶点数小于有向图中的顶点数,则说明有向图中存在回路,否则输出的顶点的顺序即为一个拓扑序列。
  1. 构造一个队列Q(queue) 和 拓扑排序的结果队列T(topological);

  2. 把所有没有依赖顶点的节点放入Q;

  3. 当Q还有顶点的时候,执行下面步骤:

    • 从Q中取出一个顶点n(将n从Q中删掉),并放入T(将n加入到结果集中);
    • 对n每一个邻接点m(n是起点,m是终点);
    • 去掉边<n,m>;
    • 如果m没有依赖顶点,则把m放入Q;

    注:顶点A没有依赖顶点,是指不存在以A为终点的边。


题目分析

  • 本题的数字电路抽象出来就是有向图,这就需要构建图(常用有邻接表、邻接矩阵两种),此题目采用邻接矩阵的方法;
  • 用一个二维矩阵来存储一条边的起点和终点。
  • 中间的过程是模拟作为边的入度的点使用位运算计算其值作为该点的权值,最后输入的结果就是类似于计算点权重;
  • 题目中体现到并不是所有的器件组合都能够有正确的输出,只要连接关系上存在环路,电路就无法正常运行,这就想到了有向无环图,与有向无环图最直接相关的便是拓扑排序算法。在拓扑排序算法执行的过程中,我们既可以判断是否存在环路,没有环路我们又可以得到一个拓扑排序,也就是元器件之间的计算顺序。
  • 使用拓扑排序算法就离不开节点的入度信息,所以要声明一个入度数组来存储所有器件(节点)的入度信息
  • 简单来说,此题就是邻接矩阵+拓扑排序+逻辑运算的一点知识。

解题步骤

前言:我们用Java去编写题目,经常会遇到需要定义一个实体类来解决问题的情况,大家通常习惯于在Main类的外面定义一个类,这样就需要严格生成GetterAndSetter,使得代码变得冗长。我们可以采取另外一个方法,在Main类的内部定义静态内部类并把属性设定为public,这样就不需要相关GetterAndSetter,因为可以在main函数中直接操作对象的属性。

  1. 完成所有电子器件的属性输入以及对象创建,具体包括每个器件的功能类型、输入个数、来自外界的输入、来自器件的输出转化输入、邻接矩阵以及入度表的数值填充、构建器件动态数组metaList。
  2. 完成所有输入数据以及期望输出信息的输入,具体包括总的输入组数、每组输入的具体数值、每组期望哪些器件参与输出以及全部组别的输入输出的动态数组。
  3. 根据拓扑排序算法判断是否存在环路并且生成拓扑排序。
  4. 根据拓扑排序依次处理每组输入输出填充到每个器件的states属性中并执行计算产生输出,并且记得计算完一组输入输出后需要清空所有器件的states与result。

public class demo11 {
    //静态内部类Meta用来表示器件
    public static class Meta{
        public String type;//每个器件的功能类型
        public List<Integer> inputs;   // 来自外部的输入对应I
        public List<Integer> receive; // 来自其他器件的输入对应O
        public int totalInputNum;//总的输入个数
        public List<Boolean> states;  // 某次输入后 所有输入端的状态,注意在下次运行的时候要清空
        public Boolean result = null;   // 器件输出结果,也要记得清空

        //方便调试输出
        @Override
        public String toString() {
            return "Meta [type=" + type + ", inputs="
                    + inputs + ", recieve=" + receive + "] result = " + result;
        }

        //某次输入后,根据states的值与功能类型计算结果
        //NOT非;OR或;XOR异或;AND与;NAND与非;NOR或非
        public void calcuResult(){
            switch(type) {
                case "NOT":
                    result = !states.get(0);
                    break;
                case "OR":
                    result = states.get(0);
                    //为什么要单独摘出来一个,是因为该属性初始化为null
                    for(int i = 1; i < states.size(); i++) {
                        result |= states.get(i);
                    }
                    break;
                case "XOR":
                    result = states.get(0);
                    for(int i = 1; i < states.size(); i++) {
                        result ^= states.get(i);
                    }
                    break;
                case "AND":
                    result = states.get(0);
                    for(int i = 1; i < states.size(); i++) {
                        result &= states.get(i);
                    }
                    break;
                case "NOR":
                    result = states.get(0);
                    for(int i = 1; i < states.size(); i++) {
                        result |= states.get(i);
                    }
                    result = !result;
                    break;
                case "NAND":
                    result = states.get(0);
                    for(int i = 1; i < states.size(); i++) {
                        result &= states.get(i);
                    }
                    result = !result;
                    break;
            }
        }
    }

    public static void main(String[] args){
        //第一部分
        Scanner scanner = new Scanner(System.in);
        int Q = scanner.nextInt();//Q表示数电问题个数
        for(int i=0;i<Q;i++){
            int M = scanner.nextInt();//输入个数
            int N = scanner.nextInt();//器件个数
            //邻接矩阵用来存储不同器件之间的输入输出关系
            //比如graph[0][1]=true表示器件0的输出作为器件1的输入
            boolean[][] graph = new boolean[N][N];
            //入度表,用于拓扑排序判断是否有环中
            int[] intoDegree = new int[N];
            //用来存放所有器件Meta的动态数组
            List<Meta> metaList = new ArrayList<>();
            for(int j=0;j<N;j++){
                Meta meta = new Meta();//每次循环生成一个Meta
                String type = scanner.next();//功能类型
                int t2 = scanner.nextInt();//输入数量
                List<Integer> input = new ArrayList<>();//外部输入数组对应的I
                List<Integer> receive = new ArrayList<>();//其他器件输入数组对应的O
                for(int m=0;m<t2;m++) {
                    String t3 = scanner.next();
                    if(t3.charAt(0) == 'I') {
                        input.add(Integer.parseInt(t3.substring(1)));
                    }else {
                        int index = Integer.parseInt(t3.substring(1))-1;
                        receive.add(index);
                        graph[index][j] = true;//从index到i由输出到输入
                        //在输入的同时就计算出,每一个节点的入度
                        intoDegree[j]++;//入度+1
                    }
                }
                meta.type = type;
                meta.totalInputNum = t2;
                meta.inputs = input;
                meta.receive = receive;
                meta.states = new ArrayList<>();
                metaList.add(meta);//将每次循环产生的meta添加到metaList动态数组中
            }
            //第二部分
            int S = scanner.nextInt();//输入的组数
            List<List<Integer>> allInputs = new ArrayList<>();
            List<List<Integer>> allResults = new ArrayList<>();
            //S个输入信号的状态(0或1)
            for(int m=0;m<S;m++) {
                List<Integer> temp1 = new ArrayList<>();
                for(int n=0;n<M;n++) {
                    temp1.add(scanner.nextInt());
                }
                allInputs.add(temp1);
            }
            //S个行为输出描述(输出的信号数量|对应输入下组合逻辑完成计算后需要输出结果的器件号)
            for(int m=0;m<S;m++) {
                List<Integer> temp2 = new ArrayList<>();
                int t4 = scanner.nextInt();//需要输出的器件个数
                temp2.add(t4);
                for(int n=0;n<t4;n++) {
                    temp2.add(scanner.nextInt());
                }
                allResults.add(temp2);
            }
            //判断环路
            Queue<Integer> queue = new LinkedList<>();//保存入度为0的节点(也就是器件)
            List<Integer> sortOrder = new LinkedList<>(); // 拓扑排序序列,保存的是元件的下标
            for (int u = 0; u < N; u++) {
                if (intoDegree[u] == 0) {
                    queue.add(u);
                    sortOrder.add(u);
                }
            }
            while(!queue.isEmpty()) {
                int node = queue.poll(); // 弹出一个元素
                for (int u = 0; u < N; u++) {
                    if (graph[node][u]) {//搜索入度为0的点,以此为起点的边
                        intoDegree[u]--;
                        graph[node][u] = false; // 删除这条边
                        if (intoDegree[u] == 0) {//如果删除该边,入度-1之后入度为0需要加入queue与sortOrder
                            queue.add(u);
                            sortOrder.add(u);
                        }
                    }
                }
            }
            //若此时输出的顶点数小于有向图中的顶点数,则说明有向图中存在回路
            if (sortOrder.size() != N) {
                System.out.println("LOOP");
                continue ;
            }
            for(int u = 0; u < S; u++) {
                List<Integer> inputs = allInputs.get(u);//获得第u组输入
                List<Integer> results = allResults.get(u);//获得第u组输出

                for (Integer metaIndex : sortOrder) {//根据拓扑排序来确定计算顺序
                    Meta meta = metaList.get(metaIndex);//获得当前应该计算的meta
                    for(Integer inputIndex : meta.inputs) {
                        meta.states.add(inputs.get(inputIndex - 1) == 1);//I1对应的是查该组输入中1-1=0处索引的值是否为1
                    }
                    for(Integer receiveIndex : meta.receive) {
                        meta.states.add(metaList.get(receiveIndex).result);//输出result本来就是boolean,并且receive下标从0开始
                    }
                    meta.calcuResult();
                }
                for(int r = 1; r <= results.get(0); r++) {
                    System.out.print(metaList.get(results.get(r) - 1).result == true ? 1 : 0);
                    System.out.print(" ");
                }
                // 清空之前的状态
                metaList.forEach( meta ->  {
                    meta.states = new ArrayList<>();
                    meta.result = null;
                });
                System.out.println();
            }
        }
    }
}



get到的知识小点

  • integer.parseint方法将字符串转换为数值,它实际上有两个方法,参数不同:

    parseInt(String s, int radix): s表示字符串、radix表示字符串数值的进制

    parseInt(String s):相当于parseInt(s, 10),默认是10进制

  • valueOf()

    static Integer valueOf(int i)
    static Integer valueOf(String s)
    static Integer valueOf(String s, int radix)
    
  • Lambda表达式

    Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用 Lambda 表达式可以使代码变的更加简洁紧凑。

    //语法格式
    //由三部分组成:参数列表,箭头(->),以及一个表达式或语句块。
    (parameters) -> expression
    或
    (parameters) ->{ statements; }
    

    它本质上是一个匿名方法。

    数组不能直接在forEach中使用lambda表达式,想要使用必须转换

    (1)转成list

    (2)转成steam

            PartnerType[] values = PartnerType.values();
            Arrays.stream(values).forEach(System.out::println);//转成流
            Arrays.asList(values).forEach(System.out::println);//转成list
    
  • list在forEach中使用lambda

            ArrayList<String> arrayList = new ArrayList<>();
            arrayList.add("a");
            arrayList.add("b");
            arrayList.add("c");
            arrayList.forEach(System.out::println);
    
  • map在forEach中使用lambda

    Map<String, Integer> items = new HashMap<>();
    items.put("A", 10);
    items.put("B", 20);
    items.put("C", 30);
    items.put("D", 40);
    items.put("E", 50);
    items.put("F", 60);
     
     
    items.forEach((k,v)->System.out.println("Item : " + k + " Count : " + v));
     
     
    items.forEach((k,v)->{
        System.out.println("Item : " + k + " Count : " + v);
        if("E".equals(k)){
            System.out.println("Hello E");
        }
    });
    

    Java8 forEach+Lambda表达式_kone666-CSDN博客


今日推歌

----《落日与晚风》

落日与晚风
深情地相拥
曾相遇的路口
记忆盘旋了很久
退后的借口
失落的相守
已无法再陪你
到黑夜之后

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星回昭以烂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值