CSP认证-202303

前言

        使用java,根据官方模拟考试的试题列表刷题 试题清单

        目前只更新了前三题的思路,并且第三题跟着找到的满分答案但只得了70分(求佬指点),后面两题先放一放,随缘更新~

202303

202303-1 田地丈量

(1)满分思路:x1和x2都分别先与0取最大值,再与a取最小值;y1和y2都分别与0取最大值,再与b取最小值;最后求面积(x2-x1)*(y2-y1)。如果x和y在规定范围内,就会保留原值,如果超出边界,就会使用边界值,并且最后计算的面积不会为负

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int num = scanner.nextInt(), a = scanner.nextInt(), b = scanner.nextInt();
        int size = 0;
        for (int i = 0; i < num; i++) {
            int x1 = scanner.nextInt(), y1 = scanner.nextInt();
            int x2 = scanner.nextInt(), y2 = scanner.nextInt();
            x1 = Math.max(x1, 0);
            x1 = Math.min(x1, a);
            x2 = Math.max(x2, 0);
            x2 = Math.min(x2, a);
            y1 = Math.max(y1, 0);
            y1 = Math.min(y1, b);
            y2 = Math.max(y2, 0);
            y2 = Math.min(y2, b);
            size += (x2 - x1) * (y2 - y1);
        }
        System.out.println(size);
    }
}

(2)满分思路:x1与0取最大,x2与a取最小;y1与0取最大,y2与b取最小;最后(x2-x1)*(y2-y1)其中值为负数时舍弃,最终结果为累加面积

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int num = scanner.nextInt(), a = scanner.nextInt(), b = scanner.nextInt();
        int size = 0;
        int x1, y1, x2, y2, x, y;
        for (int i = 0; i < num; i++) {
            x1 = scanner.nextInt();
            y1 = scanner.nextInt();
            x2 = scanner.nextInt();
            y2 = scanner.nextInt();
            x = Math.min(x2, a) - Math.max(x1, 0);
            y = Math.min(y2, b) - Math.max(y1, 0);
            if (x >= 0 && y >= 0) size += x * y;
        }
        System.out.println(size);
    }
}

202303-2 垦田计划

(1)满分思路:使用Map<Integer,Integer>存储每种时间对应的总资源;将key转换为List并使用sort逆序,从最长时间开始缩短,直至资源不够或到达最少天数

java:

        ①key数组:new ArrayList<>(Map.keySet())

        ②List降序 List.sort(Collections.reverseOrder())

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(), m = scanner.nextInt(), k = scanner.nextInt();
        Map<Integer, Integer> times = new HashMap<>();
        for (int i = 0; i < n; i++) {
            int t = scanner.nextInt(), c = scanner.nextInt();
            times.put(t, times.getOrDefault(t, 0) + c);
        }
        List<Integer> keys = new ArrayList<>(times.keySet());
        keys.sort(Collections.reverseOrder());
        int minTime = keys.get(0);
        while (minTime > k) { // 最少天数
            int tempC = times.get(minTime);
            if (m < tempC) break; // 资源不足
            times.put(minTime - 1, times.getOrDefault(minTime - 1, 0) + tempC);
            minTime--;
            m -= tempC;
        }
        System.out.println(minTime);
    }
}

(2)满分思路:二分查找,在[k,maxTime]查找资源能满足的最少天数

import java.util.*;

public class Main {
    static int[][] times; // 所有时间

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(), m = scanner.nextInt(), k = scanner.nextInt();
        times = new int[n][2];
        int maxTime = 0;
        for (int i = 0; i < n; i++) {
            times[i][0] = scanner.nextInt();
            times[i][1] = scanner.nextInt();
            maxTime = Math.max(maxTime, times[i][0]);
        }
        int l = k, r = maxTime, res = r;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (finish(mid, m)) {
                res = mid;
                r = mid - 1;
            } else l = mid + 1;
        }
        System.out.println(res);
    }

    public static boolean finish(int time, int m) {
        int sumM = 0;
        for (int i = 0; i < times.length; i++) {
            if (times[i][0] < time) continue;
            sumM += (times[i][0] - time) * times[i][1];
            if (sumM > m) return false; // 资源不够
        }
        return true;
    }
}

202303-3 LDAP

(1)40分思路:内存超限。使用Map<Integer,Map<Integer,Integer>>记录用户dn->属性号->属性值;char[]存储每个匹配表达式;match()递归处理匹配表达式,index激励记录下一个处理位置的下标;遇到&或|要匹配括号,并处理括号内结果的并交集,遇到数字获取两个操作数和一个操作符,使用getMatch()获得原子表达式结果;结果升序。求佬指出改进思路

注意:操作数不止一个char;结果要升序Collections.sort(List)

import java.util.*;

public class Main {
    static Map<Integer, Map<Integer, Integer>> users = new HashMap<>(); // 用户及属性

    static int length, index;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // 用户
        int n = scanner.nextInt();
        for (int i = 0; i < n; i++) { // 每个用户
            int dn = scanner.nextInt(), attrNum = scanner.nextInt();
            Map<Integer, Integer> attrMap = new HashMap<>();
            for (int t = 0; t < attrNum; t++) attrMap.put(scanner.nextInt(), scanner.nextInt());// 每个属性
            users.put(dn, attrMap);
        }
        // 匹配表达式
        int m = scanner.nextInt();
        scanner.nextLine(); // 清空换行符
        for (int i = 0; i < m; i++) { // 每个匹配表达式
            char[] expr = scanner.nextLine().toCharArray(); // 匹配表达式
            length = expr.length;
            index = 0;
            List<Integer> res = match(expr);
            if (res != null) {
                Collections.sort(res);
                for (int r : res) {
                    System.out.print(r + " ");
                }
            }
            System.out.println();
        }
    }

    public static List<Integer> match(char[] expr) {
        char current = expr[index];
        if (current == '&' || current == '|') {
            index += 2;//跳过左括号
            List<Integer> list1 = match(expr);
            index += 2;//跳过右左括号
            List<Integer> list2 = match(expr);
            index += 1; //跳过右括号
            if (current == '&') {
                if (list1 == null || list2 == null) return null;
                list1.retainAll(list2);
            } else {
                if (list1 == null && list2 == null) return null;
                if (list1 == null || list2 == null) return list1 == null ? list2 : list1;
                list1.removeAll(list2);
                list1.addAll(list2);
            }
            return list1;
        } else { // 遇到数字
            StringBuilder num1 = new StringBuilder(), num2 = new StringBuilder();
            num1.append(current);
            index++;
            while (index < length && Character.isDigit(expr[index]))
                num1.append(expr[index++]);
            char op = expr[index++];
            while (index < length && Character.isDigit(expr[index]))
                num2.append(expr[index++]);
            return getMatch(Integer.parseInt(num1.toString()), op, Integer.parseInt(num2.toString()));
        }
    }

    public static List<Integer> getMatch(int attrCode, char op, int attrValue) {
        List<Integer> matchedUsers = new ArrayList<>();
        if (op == ':') {
            for (int dn : users.keySet()) { // 每个用户
                if (users.get(dn).containsKey(attrCode) && users.get(dn).get(attrCode) == attrValue) {
                    matchedUsers.add(dn);
                }
            }
        } else if (op == '~') {
            for (int dn : users.keySet()) { // 每个用户
                if (users.get(dn).containsKey(attrCode) && users.get(dn).get(attrCode) != attrValue) {
                    matchedUsers.add(dn);
                }
            }
        }
        return matchedUsers;
    }
}

(2)70分思路:错误。由于结果是获得一个用户dn的列表,将信息存储改为Map<Integer,Map<Integer,List<Integer>>>记录属性号->属性值->用户dn列表;上个结构只能找到有该属性且值为指定值的dn列表,即:操作符,很难找有该属性值但结果不为指定值的dn列表,即~操作符,使用Map<Integer,List<Integer>>记录属性号->用户dn列表;对匹配表达式的操作与上个方法相同;结果升序。(还未找出代码在哪里出问题了,求佬指点)

注意:直接返回Map中get的List并对其操作,会改变原有List中的数据,因此getMatch()和match()中return的结果是new ArrayList<>(List)

import java.util.*;

public class Main {
    static Map<Integer, Map<Integer, List<Integer>>> attrIdMap = new HashMap<>(); // 属性->属性值->用户List
    static Map<Integer,List<Integer>> have = new HashMap<>(); // 属性->用户号

    static int length, index;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // 用户
        int n = scanner.nextInt();
        for (int i = 0; i < n; i++) { // 每个用户
            int dn = scanner.nextInt(), attrNum = scanner.nextInt();
            for (int t = 0; t < attrNum; t++) {
                int attrId = scanner.nextInt(), attrValue = scanner.nextInt();
                // attrIdMap
                Map<Integer,List<Integer>> attrValueMap = attrIdMap.getOrDefault(attrId,new HashMap<>());
                List<Integer> userList = attrValueMap.getOrDefault(attrValue, new ArrayList<>());
                userList.add(dn); // 用户编号
                attrValueMap.put(attrValue,userList);
                attrIdMap.put(attrId, attrValueMap);
                // have
                List<Integer> haveList = have.getOrDefault(attrId,new ArrayList<>());
                haveList.add(dn);
                have.put(attrId,haveList);
            }
        }
        // 匹配表达式
        int m = scanner.nextInt();
        scanner.nextLine(); // 清空换行符
        for (int i = 0; i < m; i++) { // 每个匹配表达式
            char[] expr = scanner.nextLine().toCharArray(); // 匹配表达式
            length = expr.length;
            index = 0;
            List<Integer> res = match(expr);
            if (res != null) {
                Collections.sort(res);
                for (int r : res) {
                    System.out.print(r + " ");
                }
            }
            System.out.println();
        }
    }

    public static List<Integer> match(char[] expr) {
        char current = expr[index];
        if (current == '&' || current == '|') {
            index += 2;//跳过左括号
            List<Integer> list1 = match(expr);
            index += 2;//跳过右左括号
            List<Integer> list2 = match(expr);
            index += 1; //跳过右括号
            if (current == '&') {
                if (list1 == null || list2 == null) return null;
                list1.retainAll(list2);
            } else {
                if (list1 == null && list2 == null) return null;
                if (list1 == null || list2 == null) return list1 == null ? list2 : list1;
                list1.removeAll(list2);
                list1.addAll(list2);
            }
            return list1;
        } else { // 遇到数字
            StringBuilder num1 = new StringBuilder(), num2 = new StringBuilder();
            num1.append(current);
            index++;
            while (index < length && Character.isDigit(expr[index]))
                num1.append(expr[index++]);
            char op = expr[index++];
            while (index < length && Character.isDigit(expr[index]))
                num2.append(expr[index++]);
            return getMatch(Integer.parseInt(num1.toString()), op, Integer.parseInt(num2.toString()));
        }
    }

    public static List<Integer> getMatch(int attrCode, char op, int attrValue) {
        List<Integer> haveUsers = have.getOrDefault(attrCode,new ArrayList<>()); // 有该属性
        List<Integer> matchedUsers = attrIdMap.getOrDefault(attrCode,new HashMap<>())
                .getOrDefault(attrValue,new ArrayList<>()); // 等于
        if(!haveUsers.isEmpty()){ // 包含属性
            if(op == ':' && matchedUsers!=null) {
                return new ArrayList<>(matchedUsers);
            }
            else if (op == '~') {
                List<Integer> users = new ArrayList<>(haveUsers);
                if(matchedUsers!=null) users.removeAll(matchedUsers);
                return users;
            }
        }
        return null;
    }
}

202303总结

        1、第一题使用min和max将坐标变到合理区间,避免多个if判断

        2、第二题将问题转换为记录每种时间对应的总资源并逐个判断,或者使用二分查找

        3、第三题使用递归处理匹配表达式,注意根据算法结果来设计合适的数据结构,比如题目要返回满足属性条件的用户dn列表,使用Map<Integer,Map<Integer,List<Integer>>>来存储信息

        4、注意题目要求,返回结果为升序

        5、直接返回Map中的List并处理,会改变List中的结果,使用new ArrayList<>(List)备份后返回

        6、java中List排序,升序Collections.sort(List),降序List.sort(Collections.reverseOrder())

  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值