poj1013

来源:互联网 发布:炉石传说卡背淘宝 编辑:程序博客网 时间:2024/05/21 14:51

本题问题分析部分参考http://blog.csdn.net/lyy289065406/article/details/6661421


大致题意:

有一打(12枚)硬币,其中有且仅有1枚假币,11枚真币

用A~L作为各个硬币的代号

假币可能比真币略轻,也可能略重

现在利用天枰,根据Input输入的3次称量,找出假币,并输出假币是轻还是重。

 

解题思路:

模拟法要考虑的情况较繁琐,可利用简单的逻辑推理进行解题。

 

 

注意Input一行代表一次称量,每行有三个字符串,分别为

Left   right     status

代表该次称量时,天枰左盘放的硬币、天枰右盘放的硬币、天枰右盘的状态

 

共三种状态:

Up:右盘上升,说明右盘可能有轻假币,也可能左盘有重假币。

Down:右盘下降,说明右盘可能有重假币,也可能左盘有轻假币。

Even:右盘与左盘平衡,由于假币有且仅有1枚,则说明此时天枰两边的硬币全为真币。

 

注意题目的字眼:

1、  有且仅有1枚假币

2、  假币相对于真币的重量,可能轻可能重

3、  只称量3次,且称量3次恰好且必能找到假币

4、  每次称量时天枰两边的硬币数目一样

5、  选取哪些硬币称量由input决定

 

从3、4、5可知,由于无法知道每次选取称量的硬币,那么3次称量可能只选用了几个硬币,也可能仅有一两个硬币没有选上,那么用模拟法去记录每次用于称量的硬币的状态(真假,其中假币又有轻重之分)并推导没有被称量的硬币状态(或状态变化)是很困难的,虽然人很容易做到这点,但计算机却很难去“推导”,因为称量硬币的方法是无规律的且非常多。

 

那么只能通过适当转化问题后用另一种有效的方法去解决。

 

虽然称量硬币的方法是无规律且未知的,但是称量硬币后的结果却只有3个,up、down和 even。且当出现even时,天枰两边的硬币必然都为真币,假币必定在余下的硬币之间(这是因为假币有且只有一枚),那么我们就可以定义一个标记数组 zero[]去标记even时的真币,在以后的处理把他们排除在外。

而唯一难以处理的是up和down的状态,因为假币可能轻可能重,则这两种状态都无法得知究竟假币出现在天枰的哪边。

 

处理up和down状态方法:

当出现up或down状态时,天枰两边的所有硬币都应该被怀疑为假币(已标记必定为真币的硬币不必被怀疑)。

首先time[]记录每个硬币的被怀疑程度,time[i]=0表示该硬币i不被怀疑(即其可能为真币)。定义在up状态盘的硬币为“轻怀疑假币”,通过“--”操作加深其被怀疑为轻假币的程度,“负号”为轻假币的怀疑方向;在down状态盘的硬币为“重怀疑假币”,通过“++”操作加深其被怀疑为重假币的程度,“正号”为重假币的怀疑方向。

那么若一枚真币被怀疑为“轻假币”时,它就可能通过下次称量通过“++”操作取消嫌疑了。初始化所有硬币的怀疑程度均为0。

称量完毕后,找出被怀疑程度最大(注意取绝对值)的硬币,它就是假币。而当其怀疑方向为正时,则其为重假币。为负时,为轻假币。


package com.njupt.acm;


import java.util.*;


public class POJ1013 {
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int N = cin.nextInt();
while (N-- > 0) {
plate[][] p = new plate[4][4];
for (int i = 1; i <= 3; i++){
for (int j = 1; j <= 3; j++){
p[i][j] = new plate(cin.next());
}
}
char t = 'A';
while (t <= 'L') {
int count = 0;
for (int i = 1;i <= 3; i++){

/**
* p[i][1].左盘的序列
* p[i][2].右盘的序列
* p[i][3].比较结果
*/
if (judge(p[i][1], p[i][2], t, true)
.equals(p[i][3].get_s())){
count++;
}
}
if (count == 3){
System.out.println(t
+ " is the counterfeit coin and it is heavy.");
break;
}


count = 0;
for (int i = 1; i <= 3; i++){
if (judge(p[i][1], p[i][2], t, false).equals(
p[i][3].get_s())){
count++;
}
   }
if (count == 3) {
System.out.println(t
+ " is the counterfeit coin and it is light.");
break;
}
t++;
}


}
}


/**

* judge(plate a, plate b, char c, boolean heavy) ,其实,该方法采取了假设法。
* 主要逻辑如下:
* 假设c为假币,用heavy属性来标识他的重量是比正常的重还是轻.

* 1)假如a盘和b盘都不包含c,则返回even.(有同学可能会有疑问,为什么不包含c,就返回even呢?
* 因为,由题意可知,只有一枚假币,每次比较只是从所有硬币中取出其中的一部分进行比较.所以,
* 假如两个盘子都不含有假币的猜测值,那我们就猜测拓为even
* )

* 2)如果他是重假币(也就是家必得质量比正常硬币的重量要重),那么如果左边的盘子包含它,那么右边的盘子就会up,
* 如果左边的盘子不包含它那么右边的盘子就会down

* 3)假如他是请假币,类比2)进行分析·······



* @param a 左边盘子的字符串序列
* @param b 右边盘子的字符串序列 
* @param c 假设的假币
* @param heavy 假设假币的重量属性
* @return
*/
public static String judge(plate a, plate b, char c, boolean heavy) {
//如果a、b中都不包含c,则打印出even
if (!a.contain(c) && !b.contain(c)){
return "even";
}
else {

if (heavy) {
//如果a包含了c,则右边盘的状态为:up
if (a.contain(c)){
return "up";
}
else{
//否则右边盘的状态为down
return "down";
}
} else {
//以下是对轻的情况进行处理

//如果a包含c,则右边的盘的状态变为down
if (a.contain(c)){
return "down";
}
else{
//否则,右边的潘德状态变为up
return "up";
}
}
}
}
}


class plate {
private String s;



/**
* contain(char a).如果该盘子包含硬币a则返回true,否则返回false

* @param a 某一枚硬币
* @return
*/
boolean contain(char a) {


//String.valueOf(a),将a转化成字符串
if (s.contains(String.valueOf(a))){


return true;
}
else{

return false;
}
}




/**
* 判断两个字符串是否相等
* @param t
* @return
*/
boolean equal(String t) {
if (s.equals(t)){
return true;
}
else{
return false;
}
}


/**
* plate类的构造方法
* @param t
*/
plate(String t) {
s = t;
}


/**
* 获得盘子里面的字符串
* @return
*/
String get_s() {
return s;
}


}

原创粉丝点击