Java 100道经典机试笔试题(08)——附可运行代码

导语

每篇将有两道经典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结果
011
000
101
110

满足以上运算结果的操作显然是“异或”,那么这道题就可以转化为判断某些圆盘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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值