昨天又從某處看到了12球稱重的問題,雖然之前有過解題的過程,但這次仍然費了不少時間才又找到解題方法。然後我又思考,從我看到問題,到得到解決方法,這是怎樣的一個過程,怎樣的思維想到了怎樣一個方法,從而得到了怎樣的一個結果。假如我能夠把這個過程描述下來,那可謂對自己的思維過程有了一個很好的梳理,並且可以記錄入自己的SOP;同時,也就更加真實地瞭解自己。而更爲重要的是,可以將這種思維的過程,傳播出去;從而不僅僅是知識得到延續,而更爲重要的思維過程,也可以得到延續。
題目:12個球,外觀(大小、色澤等)完全一致,其中,有十一個球的重量是一樣的,另一個球的重量與其他球是不一致的。怎樣用天平最多僅稱重三次,找出那個重量不一致的球,並且還能確認這個球是較重還是較輕。
這個題的重點是不知道那個不一致的球是重了還是輕了。那假如是這個球是重的呢?
很多人的思路是,平分成兩組,每組6個,進行稱重。然後將重的一組再分組,每組3個,進行稱重。最後,將重的一組(3個球)其中兩個稱重,若有一個重,則這個球是要找的球;若相等,則沒有稱重的這個球是要找的球。
但是這個過程是無法直接應用到要解決的問題上的。所以,只能換一個方向——簡化。
兩個球的情況:無法得知哪個球是要找的球。
三個球的情況:標記ABC,取AB進行稱重,會有兩種情況:
1,它們相等,那麼C就是要找的球,稱A和C
1.1 C>A,C較重。
1.2 C<A,C較輕
1.3 C=A,A=B=C,與前提矛盾,因此不可能出現。
2,它們不等,我們假設A>B,那麼C是一顆正常的球。將A與C稱重,
2.1相等,B球較輕,爲要找的球。
2.2A>C,A球較重,爲要找的球。
2.3A<C,那麼C>A>B,與前提矛盾,因此不可能出現。
然後再回過頭來,看上面的過程。你會發現什麼,先想一想吧……
這其實是一個信息獲取的過程,根據已知信息,通過操作,得到未知信息。如果我們一直測AB的話,即使測100遍也無法找到重量較輕或者較重的那個球。
可以通過一種標記來記錄信息,這裏球有正常,較輕和較重三種情況,可以用(+1,0,-1)來表示。當通過一次次的測量,爲各個球標上(+1,0,-1)三種狀態,並最終有且僅有一個球的狀態爲+1或者-1,就可以確定,這個狀態爲+1或者-1的球爲要找的較重或者較輕的球。爲上述操作過程分別標記信息含量值
1,它們相等,那麼C就是要找的球[A0,B0,C]
稱A和C
1.1 A<C,C重[A0,B0,C+]
1.2 A>C,C輕[A0,B0,C-]
1.3 C=A,A=B=C,與前提矛盾,因此不可能出現。
2,它們不等,我們假設A>B,那麼C是一顆正常的球。[A+,B-,C0]
將A和C
2.1 A=C,B輕[A0,B-,C0]
2.2 A>C,A重。可由A>C和前提得知B=C,因此[A+,B0,C0]
2.3 A<C,那麼C>A>B,與前提矛盾,因此不可能出現。
那麼,總結一下整個過程,就是將球分爲較重、較輕(標記+-),並通過和標準球(標記0)的比較,來得知是否爲重球(不是重球,則與之相比較的球是輕球)或者輕球。
再來分析一下標記的信息值,0表示爲標準球,含有確切含義,而+和-在不唯一的情況下,只能表示可能較重,但其實並不重,按其信息值排序的話:0>(+或-)>沒有標記,我們的目的是在每次稱重的過程中,使獲得的信息值最大化。
12球的過程
6:6稱重,只能將6個球標記爲+,另6個球標記爲-,無法得到0的標記
而4:4稱重,至少可以將4個球標記爲0(其實,有兩種可能,一是8個0,另一種是4個+,4個-,4個0)
package com.liubuhu.twevel;
import java.util.Scanner;
public class TwevelBalls {
private static final int LIGHT = -1;
private static final int NORMAL = 0;
private static final int HEAVY = 1;
private Integer[] balls = null;
public Integer[] getBalls() {
if (balls == null) {
balls = new Integer[12];
for (int i = 0; i < balls.length; i++) {
balls[i] = 10;
}
}
return balls;
}
public TwevelBalls() {
}
private class InputBean {
private Boolean bfind;
private Integer ball;
private Integer heavy;
private Integer[] left;
private Integer[] right;
public Boolean getBfind() {
return bfind;
}
public void setBfind(Boolean bfind) {
this.bfind = bfind;
}
public Integer getBall() {
return ball;
}
public void setBall(Integer ball) {
this.ball = ball;
}
public Integer[] getLeft() {
return left;
}
public void setLeft(Integer[] left) {
this.left = left;
}
public Integer[] getRight() {
return right;
}
public void setRight(Integer[] right) {
this.right = right;
}
public void setHeavy(Integer heavy) {
this.heavy = heavy;
}
public Integer getHeavy() {
return heavy;
}
}
/**
* @param args
*/
public static void main(String[] args) {
TwevelBalls t = new TwevelBalls();
t.run();
}
private void run() {
Integer theBall = (int) Math.floor(Math.random() * 12);
// 1 for heavy, -1 for light
Integer heavy;
if (Math.random() > 0.5) {
heavy = HEAVY;
} else {
heavy = LIGHT;
}
Integer[] myBalls = getBalls();
System.out.println("遊戲開始:");
System.out.println("遊戲示例,第二個球比較重請輸入:Y;2;1");
System.out.println("遊戲示例,第十個球比較輕請輸入:Y;10;-1");
System.out.println("遊戲示例,左右兩邊分別稱1,2,3,4和5,6,7,8請輸入:N;1,2,3,4;5,6,7,8");
InputBean bean = null;
Scanner scan = new Scanner(System.in);
// 1
bean = getInput(scan);
myBalls = processData(myBalls, bean, theBall, heavy);
// 2
bean = getInput(scan);
myBalls = processData(myBalls, bean, theBall, heavy);
// 3
bean = getInput(scan);
myBalls = processData(myBalls, bean, theBall, heavy);
// 4 結果
bean = getInput(scan);
myBalls = processData(myBalls, bean, theBall, heavy);
// 結束
scan.close();
// System.out.println("遊戲結束,開始頒獎");
}
private Integer[] processData(Integer[] myBalls, InputBean bean,
Integer theBall, Integer heavy) {
if (bean.getBfind()) {
if (check(myBalls)) {
if (theBall.equals(bean.getBall())
&& heavy.equals(bean.getHeavy())) {
System.out.println("你真厲害,你答對了!");
} else {
System.out.println("差了一點點,你是不是敲錯鍵盤了");
}
} else {
if (theBall.equals(bean.getBall())
&& heavy.equals(bean.getHeavy())) {
System.out.println("你運氣太好了,竟然猜對了!");
} else {
System.out.println("Sorry,你猜錯了!");
}
}
} else {
myBalls = processBalls(myBalls, bean, theBall, heavy);
}
outputBalls(myBalls);
return myBalls;
}
private void outputBalls(Integer[] myBalls) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < myBalls.length; i++) {
sb.append(Integer.valueOf(i + 1).toString()).append(":").append(
Integer.valueOf(myBalls[i]).toString()).append("; ");
}
System.out.println(sb.toString());
}
private Integer[] processBalls(Integer[] myBalls, InputBean bean,
Integer theBall, Integer heavy) {
// 左邊有特殊球
if (inTheArray(bean.getLeft(), theBall)) {
if (heavy.equals(Integer.valueOf(0))) {
// 左邊重,右邊輕
myBalls = labelBalls(myBalls, bean, HEAVY);
System.out.println("左邊重,右邊輕");
} else {
// 左邊輕,右邊重
myBalls = labelBalls(myBalls, bean, LIGHT);
System.out.println("左邊輕,右邊重");
}
// 右邊有特殊球
} else if (inTheArray(bean.getRight(), theBall)) {
if (heavy.equals(Integer.valueOf(0))) {
// 左邊輕,右邊重
myBalls = labelBalls(myBalls, bean, LIGHT);
System.out.println("左邊輕,右邊重");
} else {
myBalls = labelBalls(myBalls, bean, HEAVY);
// 左邊重,右邊輕
System.out.println("左邊重,右邊輕");
}
}
// 沒有特殊球
else {
myBalls = labelBalls(myBalls, bean, 0);
System.out.println("兩邊平衡");
}
return myBalls;
}
private Integer[] labelBalls(Integer[] myBalls, InputBean bean, int l) {
boolean[] labeled = new boolean[myBalls.length];
for (int i = 0; i < myBalls.length; i++) {
labeled[i] = false;
}
switch (l) {
case NORMAL:
for (int i = 0; i < bean.getLeft().length; i++) {
myBalls[bean.getLeft()[i]] = NORMAL;
}
for (int i = 0; i < bean.getRight().length; i++) {
myBalls[bean.getLeft()[i]] = NORMAL;
}
break;
case HEAVY:// 左邊重,右邊輕
for (int i = 0; i < bean.getLeft().length; i++) {
if (myBalls[bean.getLeft()[i]] == LIGHT
|| myBalls[bean.getLeft()[i]] == NORMAL) {
myBalls[bean.getLeft()[i]] = 0;
} else {
myBalls[bean.getLeft()[i]] = HEAVY;
}
labeled[bean.getLeft()[i]] = true;
}
for (int i = 0; i < bean.getRight().length; i++) {
if (myBalls[bean.getRight()[i]] == HEAVY
|| myBalls[bean.getRight()[i]] == NORMAL) {
myBalls[bean.getRight()[i]] = NORMAL;
} else {
myBalls[bean.getRight()[i]] = LIGHT;
}
labeled[bean.getRight()[i]] = true;
}
for (int i = 0; i < myBalls.length; i++) {
if (labeled[i]) {
} else {
myBalls[i] = 0;
}
}
break;
case LIGHT:// 左邊輕,右邊重
for (int i = 0; i < bean.getLeft().length; i++) {
if (myBalls[bean.getLeft()[i]] == HEAVY
|| myBalls[bean.getLeft()[i]] == NORMAL) {
myBalls[bean.getLeft()[i]] = NORMAL;
} else {
myBalls[bean.getLeft()[i]] = LIGHT;
}
labeled[bean.getLeft()[i]] = true;
}
for (int i = 0; i < bean.getRight().length; i++) {
if (myBalls[bean.getRight()[i]] == LIGHT
|| myBalls[bean.getRight()[i]] == NORMAL) {
myBalls[bean.getRight()[i]] = NORMAL;
} else {
myBalls[bean.getRight()[i]] = HEAVY;
}
labeled[bean.getRight()[i]] = true;
}
for (int i = 0; i < myBalls.length; i++) {
if (labeled[i]) {
} else {
myBalls[i] = NORMAL;
}
}
break;
}
return myBalls;
}
private boolean inTheArray(Integer[] array, Integer theBall) {
for (int i = 0; i < array.length; i++) {
if (theBall.equals(array[i])) {
return true;
}
}
return false;
}
private boolean check(Integer[] myBalls) {
boolean retVal = true;
int count = 0;
for (int i = 0; i < myBalls.length; i++) {
if (!Integer.valueOf(0).equals(myBalls[i])) {
count++;
}
}
if (count > 1) {
retVal = false;
System.out.println("哈哈,你開始猜了。");
}
return retVal;
}
private InputBean getInput(Scanner scan) {
InputBean bean = new InputBean();
String input = scan.nextLine();
String[] splitedStr = input.split(";");
if ("Y".equals(splitedStr[0])) {
bean.setBfind(Boolean.TRUE);
} else {
bean.setBfind(Boolean.FALSE);
}
if (bean.getBfind()) {
// 進行-1處理,使符合數組
bean.setBall(Integer.valueOf(splitedStr[1]) - 1);
bean.setHeavy(Integer.valueOf(splitedStr[2]));
} else {
bean.setLeft(gnrtBallNumbs(splitedStr[1]));
bean.setRight(gnrtBallNumbs(splitedStr[2]));
}
return bean;
}
/**
* @param str
* @return
* @author liuhao
* @description : 進行-1處理,使符合數組
*/
private Integer[] gnrtBallNumbs(String str) {
String[] splitedStr = str.split(",");
Integer[] ballNumbs = new Integer[splitedStr.length];
for (int i = 0; i < splitedStr.length; i++) {
ballNumbs[i] = Integer.valueOf(splitedStr[i]) - 1;
}
return ballNumbs;
}
}