前言
使用java,根据官方模拟考试的试题列表刷题 试题清单
目前只更新了前三题的思路,并且第三题跟着找到的满分答案但只得了70分(求佬指点),后面两题先放一放,随缘更新~
202303
(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);
}
}
(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;
}
}
(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())