算法每一题,成长每一天~
C0E7 猜数字
真题链接:【持续更新】2024华为 OD 机试E卷 机考真题库清单(全真题库)
思路
Java
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
/**
* 猜数字
*/
public class C0E7 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
// 读取输入
String[] number = new String[n];
String[] keys = new String[n];
for (int i = 0; i < n; i++) {
number[i] = in.next();
keys[i] = in.next();
}
Set<Character> excepts = new HashSet<>(); // 不包含的数字
// 特殊情况
for (int i = 0; i < keys.length; i++) {
if (keys[i].contains("4A")) {
System.out.println(number[i]);
return;
}
if (keys[i].equals("0A0B")) {
for (int j = 0; j < 4; j++) {
excepts.add(number[i].charAt(j));
}
}
}
// 将输入转换成字符
char[][] numberChar = new char[n][4];
int[][] keyCount = new int[n][2];
for (int j = 0; j < n; j++) {
for (int k = 0; k < 4; k++) {
numberChar[j][k] = number[j].charAt(k);
}
keyCount[j][0] = keys[j].charAt(0) - '0'; // X
keyCount[j][1] = keys[j].charAt(2) - '0'; // Y
}
String result;
// 全量枚举,逐个比对
OUT:
for (int i = 9999; i >= 0; i--) {
result = convert(i); // 转换为4位字符串
char[] charArray = result.toCharArray();
// 排除不包含的
for (char c : charArray) {
if (excepts.contains(c)) {
continue OUT;
}
}
if (check(charArray, numberChar, keyCount)) {
System.out.println(result);
return;
}
}
System.out.println("NA");
}
/**
* 检查是否满足条件
*
* @param obj 待检查的数字
* @param numberChar 输入的数字
* @param keyCount 输入的数字对应的A B
* @return
*/
public static boolean check(char[] obj, char[][] numberChar, int[][] keyCount) {
// 每一个输入的数字,与目标数字 obj 进行比较(每一个都需要满足)
for (int i = 0; i < numberChar.length; i++) {
char[] chars = numberChar[i];
int same = keyCount[i][0];
int like = keyCount[i][1];
Set<Character> objSet = new HashSet<>();
Set<Character> numSet = new HashSet<>();
// 先判断 同位置
for (int j = 0; j < 4; j++) {
if (obj[j] == chars[j]) {
same--;
} else {
objSet.add(obj[j]);
numSet.add(chars[j]);
}
}
if (same != 0) {
return false;
}
// ----- 这里有个易错的点:谜面多个数字匹配到谜底的同一个数字,会多次-1,是错误的!!
// ----- 所以应该是匹配出一个就拿走一个,只能用 remove(),而不能用 contains()!!
for (char c : objSet) {
if (numSet.remove(c)) { // 比对一个移除一个
like--;
}
}
if (like != 0) {
return false;
}
}
// 全部满足,返回 true
return true;
}
public static String convert(int i) {
String result = String.valueOf(i);
if (i < 10) {
result = "000" + result;
} else if (i < 100) {
result = "00" + result;
} else if (i < 1000) {
result = "0" + result;
}
return result;
}
}
总结
1、敲 check()
方法的时候,发现这里边还是有点逻辑的,容易考虑错误。比如方法中备注的,多个数字与一个数字相同时,只会记1个相同。但是如果使用 contains() 方法判断,则会匹配多次!所以改成 remove() ,匹配一个移除一个。
2、【重要】 对于这种判断规则比较多的情形,考虑全量枚举(如果枚举数量级不是很大)是一种比较好的思路。
3、【重要】 随机去重用Set集合
,按位匹配用数组
。对吗?可以形成经验么?
xx:题目不难,但是写了蛮久,还是在前面思路没有完全考虑清楚!!
xx:容器多次切换(字符串数组 --> 字符数组,字符数组 --> Set集合)也比较费时间,稍有不慎容易写错。后面看看有没有更简单的方法~
算法要多练多练多练!!