导语
每篇将有两道经典Java机试题,每道题后面均为大家附上代码,每一道题目力求:
- 能够在JDK11环境下编译
- 在Eclipse JavaIDE中运行通过
- 思路易想易懂易学
- 重点代码有注释
第015题 题目名称(难度:★★☆☆☆)
题目描述:
求a/b的小数表现形式。如果a可以整除b则不需要小数点。如果是有限小数,则可以直接输出。如果是无限循环小数,则需要把小数循环的部分用"()"括起来。
两个整数a和b,其中,0 <= a <= 1000 000,1 <= b <= 10 000
输入示例:
10 1
1 2
1 3
1 7
输出示例:
10
0.5
0.(3)
0.(142857)
思路
首先,先解释一下有些人的疑问:两个整数相除,得到的小数一定只有题目所说的三种情况吗(整除、有限小数、无限循环小数)? 难道不会出现无限不循环小数吗,就像π这个数一样?答案是:不会的。
有限小数,无限循环小数,整数都是“有理数”,而无限不循环小数则是无理数,无理数是无法用分数表示的,即无法用两个整数相除表示。换句话说,能用分数表示的数一定是有理数。
既然两个整数相除必为有理数,那么我们观察平时我们手算除法的过程,找到其中规律,即可解决此题:
假如我们计算1÷6, 那么过程如下:
第一步:上商:0,第一次余数为1
第二步,在余数添加一个0,继续上商1,此次余数为4
第三步,在余数后面添加一个0,继续上商6,此次余数为4
到这里,不难发现,此次余数与上前面某次的余数相同,这意味着循环节即将出现,于是,我们的思路来了,保存每一次的余数,如果此时余数在之前出现过,那么意味着从上一次出现的位置到此处,是一个完整的循环节,那么按照题目要求,将循环节用括号括起来即可。 思路比较简单。
代码
package demo;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Num1 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int a = sc.nextInt(); // 被除数
int b = sc.nextInt();
String list = ""; // 存小数部分的商
List<Integer> listC = new ArrayList<Integer>();// 存余数
int c; // a / b的余数
int d; // a / b的商
int e; // 保存整数部分的商
int start = 0; // 循环开始的位置
int end = 0; // 循环结束的位置
if ((0 <= a) && (a <= 1000000) && (1 <= b) && (b <= 10000)) {
c = a % b;
d = a / b;
e = d;
if (c != 0) {
listC.add(c);
for (;;) {
d = c * 10 / b;
c = (c * 10) % b;
list = list + String.valueOf(d);
if (c == 0) {
start = list.length();//除尽时,需将list全部输出
end = start;
// 除尽了,退出
break;
} else {
if (!listC.contains(c)) { // 如果该余数还未出现过,则加到列表中
listC.add(c);
} else { // 如果已经包含该数,则说明开始循环
start = listC.indexOf(c); //记录下循环节开始的位置
end = list.length();
break;
}
}
}
}
System.out.println(
// 整数部分
e
// 是否是小数
+ (list.length() == 0 ? "" : ".")
// 非循环节的小数部分
+ ((list.length() > 0) ? list.substring(0, start) : "")
// 循环节部分小数
+ (start == end ? "" : "(" + list.substring(start, end) + ")"));
}
}
}
运行结果
下面再输入一些其他的测试用例,供大家参考程序输出是否正确(这里为了让大家看得更清楚,我做了换行处理)
第016题 题目名称(难度:★★☆☆☆)
题目描述:
一群探险家被困古巴比伦迷宫 ,经过努力,探险家破译出了密码。原来通关需要先打开前方的M个机关的任意一个或多个为打开状态,然后关闭所有机关才能安全通过。探险家还发现了一种带有凸起的圆盘,圆盘可以用来同时反转若干个机关的状态(打开状态反转为关闭状态,关闭状态反转为打开状态,这若干个机关必须同时反转)。为了速记,探险家们用十六进制数字表示一个圆盘。如圆盘能同时控制第3、4、5个开关,圆盘就记为1C(11100)。每次操作一个圆盘,比特位为1的位置的机关同时被反转;而且每个圆盘使用一次后就会碎掉。目前探险家收集到了N个圆盘,问现在探险家可以顺利走出迷宫么?
若探险家手里有0、1、2、3、5五个圆盘,M=7,由于存在1 xor 2 xor 3 = 0, 因此结果是yes;
若探险家手里有0、1、2、2、5五个圆盘,M=7,由于存在2 xor 2 = 0, 因此结果是yes;
若探险家手里有0、1、3、5、9五个圆盘,M=7,由于不存组合使得 xor = 0, 因此结果是no。包含多组数据,第一行是数据组数。 每组数据中首行为M N,M为机关个数,M <= 360(大部分情况下小于40),N为圆盘个数, N<=50000;其余为N行圆盘的16进制ID,可以保证每行输入小于2^M
每组一行输出,满足条件输出yes,反之输出no
输入示例:
2 2 3 0 2 2 5 2 4 1C
输出示例:
yes no
思路
老规矩,先解释一下题目,题目看懂了就很简单。
我们就用输入示例的两组数据带入来解释:
第一组数据:
2 3
0
2
2
按照题目意思,前方有2个机关,探险家手里有3个圆盘,这三个圆盘的Id分别为0、2、2, 转换成十六进制分别为00,10,10(因为只有2个机关,所以十六进制位数有2位即可),也就是分别能打开第0个机关(也就是一个都打不开,相当于废物),第二个机关、第二个机关;
按照题目所述的通关条件:通关需要先打开前方的M个机关的任意一个或多个为打开状态,然后关闭所有机关才能安全通过,那么现有的圆盘能够通过的方式为:①先用圆盘a反转(打开)第二个机关:②再用圆盘b反转(关闭)第二个机关,所有机关均关闭,则通关成功;
第二组数据
5 2
4
1C
按照题目意思,前方有5个机关,探险家手里有2个圆盘,这两个圆盘的Id分别为4、1C, 转换成十六进制分别为00100,11100(因为有5个机关,所以十六进制位数有5位),也就是分别能打开第3个机关,第3个,第4个和第5个机关;
按照题目所述的通关条件:通关需要先打开前方的M个机关的任意一个或多个为打开状态,然后关闭所有机关才能安全通过,那么现在有两种可使用方案:①先使用圆盘a,再使用圆盘b;②先使用圆盘b,再使用圆盘b;(只使用一个显然是不可能通关的)
方案①:先使用圆盘a,将第3个机关反转(打开),再使用圆盘b,同时将第5个、第4个、第3个机关反转(打开、打开、关闭):
可以看到最后还有第5个和第4个机关是打开状态,没有满足所有机关全部关闭的要求,通关失败;
再尝试方案②:先使用圆盘b将,同时将第5个、第4个、第3个机关反转(打开、打开、打开),再使用圆盘a,将第3个机关反转(关闭);
可以看到最后还有第5个和第4个机关是打开状态,没有满足所有机关全部关闭的要求,通关失败;
分析到此,这道题目似乎就明了了许多。如果将机关初始状态记为0,反转一次记为1,那么有如下结果:
机关状态 圆盘id 结果 0 1 1 0 0 0 1 0 1 1 1 0 满足以上运算结果的操作显然是“异或”,那么这道题就可以转化为判断某些圆盘id的十六进制形式按位异或,最后结果每一位都为0,即为成功。如此解题思路瞬间清晰。
代码
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int num = sc.nextInt(); // 数据组数
List<TreasurData> listDatas = new ArrayList<>();
// 每组数据输入处理
for (int i = 0; i < num; i++) {
TreasurData data = new TreasurData();
data.setM(sc.nextInt()); // 输入M,机关个数
data.setN(sc.nextInt()); // 输入N,圆盘个数
String[] Nid = new String[data.getN()];
for (int j = 0; j < data.getN(); j++) {
Nid[j] = sc.next(); // 输入N个id号
}
data.setNid(Nid);
listDatas.add(data); // 添加到数据列表里
}
for (int i = 0; i < listDatas.size(); i++) {
// 将十六进制转为十进制
int[] nidIdDecimal = transferDecimal(listDatas.get(i).getNid());
// 按位异或得出结果
boolean result = bitwiseXOR(nidIdDecimal);
// 将结果保存起来
listDatas.get(i).setResult(result ? "yes" : "no");
}
// 输出结果
for (int i = 0; i < listDatas.size(); i++) {
System.out.println(listDatas.get(i).getResult());
}
}
public static int[] transferDecimal(String[] Nid) {
int[] nidIdBinary = new int[Nid.length];
for (int i = 0; i < Nid.length; i++) {
// 十六进制转为十进制
nidIdBinary[i] = Integer.parseInt(Nid[i], 16);
}
return nidIdBinary;
}
public static boolean bitwiseXOR(int[] nidIdDecimal) {
// 每个圆盘的Id按位异或
int num = nidIdDecimal[0];
// flag的作用是判断id号是否全0的情况,全0异或结果也为0,但显然是无法通关的;
int flag = num;
for (int i = 1; i < nidIdDecimal.length; i++) {
flag = flag + nidIdDecimal[i];
num = num ^ nidIdDecimal[i];
}
// 如果异或结果为0,且id号不全为0,则说明成功,返回true
if (flag == 0) {
return false;
} else {
return num == 0;
}
}
static class TreasurData {
private int M; // M 机关个数
private int N; // N 圆盘个数
private String[] Nid; // 圆盘Id
private String result; // 结果,是否能成功通关
public int getM() {
return M;
}
public void setM(int m) {
M = m;
}
public int getN() {
return N;
}
public void setN(int n) {
N = n;
}
public String[] getNid() {
return Nid;
}
public void setNid(String[] nid) {
Nid = nid;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
}
}
运行结果
最后还有一点想说的,
4 5
1
15
3
7
9
这组数据在某平台上的测试用例为yes,
但是我仔细研究了一下,按照题目,所输入的都是16进制的数,那么转换成16进制,分别为
1:00001
15:10101
3:00011
7:00111
9:01001
这五个数二进制数,应该没有办法组合(首先15肯定不能用,不然第5个机关异或结果一定不为0,同理9也不能用,剩下1、3、7中,7不能用,如果7不能用,那么3也不能用,不然第2位机关无法为0,只剩下1,同理1也不能用),使得最后异或结果为0的吧,所以我觉得结果是no。
这道题,欢迎大家积极贡献测试用例,若发现有不对的地方欢迎大家在评论里指出,我只要看到就会回复,谢谢。
以上是本次两道Java机试题
如有不足,欢迎批评指正
欢迎阅读上一篇:Java 100道典型机试笔试题(07)
欢迎阅读下一篇:Java 100道典型机试笔试题(09)
作者:小南瓜
日期:2020年8月16日14:35