问题描述:
有12枚硬币。其中有11枚真币和1枚假币。假币和真 币重量不同,但不知道假币比真币轻还是重。现在, 用一架天平称了这些币三次,告诉你称的结果,请你 找出假币并且确定假币是轻是重 (数据保证一定能找 出来)。
输入:
每组数据有三行,每行表示一次称量的结果。银币标号 为A-L。每次称量的结果用三个以空格隔开的字符串表示: 天平左边放置的硬币 天平右边放置的硬币 平衡状态。其 中平衡状态用"up"、"down’’, 或 "even’'表示, 分 别为右端高、右端低和平衡。天平左右的硬币数总是相等 的。
输出:
输出哪一个标号的银币是假币,并说明它比真币轻还是重。
输入样例
1
ABCD EFGH even
ABCI EFJK up
ABIJ EFGH even
输出样例
K is the counterfeit coin and it is light.
思路分析: 由于输入的三行数据一定能保证判断出那个是假币并且能判断出轻重,故,先假设当前硬币为轻的,看是否符合输入三行数据的条件,如果有一行不符合,则说明假设的硬币不可能为轻,再假设当前硬币为重的,看是否符合条件,如果有一行不符合,则说明假设的硬币也不是重的,那么该硬币一定是真的硬币,继续下次循环,根据枚举方法从第一个硬币枚举到最后一个硬币(A->L)
先看看之前我的解决方法吧,我先判断是否是假币,然后再判断是否是轻的(还需要注意一点就是:假设的硬币是否在给定的输入数据中,不在的话,直接进行下次循环)
import java.util.Scanner;
/**
* 称硬币问题
* @author codewen
*/
public class WeightCoins1 {
//判断硬币是否为真
private static boolean isFake(char c, String[] left, String[] right, String[] result) {
int count = 0;//用于计数,记录假设的硬币在左边和右边都不出现的次数
for(int i=0; i<3; i++) {
//如果假设的硬币三次都没出现在左边和右边,则说明假设的硬币不在给定的输入数据中
if(!left[i].contains(c+"") && !right[i].contains(c+"")) {
count++;
if(count == 3) return false;
}
if(result[i].equals("even")) {//天平平衡的情况
if(left[i].contains(c+"") || right[i].contains(c+"")) {//如果天平左边或者右边有假设的硬币,天平必定不平衡,假设失败
return false;
}
}else {//天平不平衡的情况
if(!left[i].contains(c+"") && !right[i].contains(c+"")) {//如果天平左边和右边都没有假设的硬币,天平必定平衡,假设失败
return false;
}
}
}
return true;//如果三行数据都符合,则说明假设成功,该硬币为假币
}
//判断假币是轻还是重
private static boolean isLight(char c, String[] left, String[] right, String[] result) {
for(int i=0; i<3; i++) {//判断假币为轻还是重,只需要找到天平不平衡的情况
if(result[i].equals("up")) {//右边轻
if(right[i].contains(c+"")) {
return true;
}
break;
}else if(result[i].equals("down")) {//左边轻
if(left[i].contains(c+"")) {
return true;
}
break;
}
}
return false;
}
public static void main(String[] args) {
System.out.println("请输入程序要执行的次数:");
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();//程序执行的次数
Scanner scanner2 = new Scanner(System.in);
String[][] lines = new String[3][3];//用来接收输入的三行三列数据
String[] left = new String[3];//用来接收三行数据的左边天平硬币
String[] right = new String[3];//用来接收三行数据的右边天平硬币
String[] result = new String[3];//用来接收三行数据天平右边的平衡状态
while(n > 0) {
n--;
System.out.println("请输入三行数据:");
for (int i = 0; i < 3; i++) {
String line = scanner2.nextLine();//接收输入的一行数据
lines[i] = line.split(" ");
left[i] = lines[i][0];
right[i] = lines[i][1];
result[i] = lines[i][2];
}
for(char c='A'; c<='L'; c++) {
if(isFake(c,left,right,result)) {//如果是假币
if(isLight(c,left,right,result)) {//是轻的
System.out.println(c+" is the counterfeit coin and it is light.");
}else {//是重的
System.out.println(c+" is the counterfeit coin and it is weight.");
}
break;
}
}
}
}
}
结果:
结果是没问题的,但这样稍微有点繁琐,其实可以简化,在判断是否是假币的时候,就可以再假设是否为轻的,只要有一次不成立,则说明假设失败,如果三次循环下来都成立,则说明假设为真,然后再假设为假的假币
import java.util.Scanner;
/**
* @author codewen
*/
public class WeightCoins2 {
private static boolean isFake(char c, String[] left, String[] right, String[] result, boolean isLight) {
String l;//用于存放左边硬币
String r;//用于存放右边硬币
int count = 0;//用于计数,记录假设的硬币在左边和右边都不出现的次数
for(int i=0; i<3; i++) {
//如果假设的硬币三次都没出现在左边和右边,则说明假设的硬币不在给定的输入数据中
if(!left[i].contains(c+"") && !right[i].contains(c+"")) {
count++;
if(count == 3) return false;
}
if(isLight) {//是轻的
l = left[i];
r = right[i];
}else {//是重的(为重的时候交换左右硬币)
l = right[i];
r = left[i];
}
switch (result[i]) {//只要三行数据中有一行数据不符合,则说明假设的硬币为真币,假设失败
case "even" ://平衡的情况
if(l.contains(c+"") || r.contains(c+"")) return false;
break;
case "up" ://右边轻
if(l.contains(c+"")) return false;
break;
case "down" ://右边重
if(r.contains(c+"")) return false;
break;
}
}
return true;
}
public static void main(String[] args) {
System.out.println("请输入程序要执行的次数:");
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();//程序执行的次数
Scanner scanner2 = new Scanner(System.in);
String[][] lines = new String[3][3];//用来接收输入的三行三列数据
String[] left = new String[3];//用来接收三行数据的左边天平硬币
String[] right = new String[3];//用来接收三行数据的右边天平硬币
String[] result = new String[3];//用来接收三行数据天平右边的平衡状态
while(n > 0) {
n--;
System.out.println("请输入三行数据:");
for (int i = 0; i < 3; i++) {
String line = scanner2.nextLine();//接收输入的一行数据
lines[i] = line.split(" ");
left[i] = lines[i][0];
right[i] = lines[i][1];
result[i] = lines[i][2];
}
for(char c='A'; c<='L'; c++) {
if(isFake(c,left,right,result,true)) {//假设是轻的假币
System.out.println(c+" is the counterfeit coin and it is light.");
break;
}else if(isFake(c,left,right,result,false)) {//假设是重的假币
System.out.println(c+" is the counterfeit coin and it is weight.");
break;
}
}
}
}
}
结果:
可以看到结果是一样的,可能会有人不理解WeightCoins2中isFake方法里为什么假设的硬币为重的时候,要将左右两边的硬币进行交换?
讨论的时候分为轻的硬币和重的硬币
如果假设为轻的硬币,result[i]是"up"的话,那么假设的硬币不能出现在左边,result[i]是"down"的话,那么假设的硬币不能出现在右边
如果假设为重的硬币,result[i]是"up"的话,那么假设的硬币不能出现在右边,result[i]是"down"的话,那么假设的硬币不能出现在左边
可以看出为重的硬币结果与为轻的硬币的情况相反,那么可以这样做,当假设为重的硬币,交换左右两边的硬币,result[i]是"up"的话,那么假设的硬币不能出现在左边,result[i]是"down"的话,那么假设的硬币不能出现在右边,这时的情况不就和为轻的硬币的情况一样吗?这样可以节省代码量、提高效率,不用写两次switch