【假币寻找】有n枚外形相同的硬币,其中有一枚是假币,假币的重量比真币轻,但是目前仅有一台无砝码的天平。请设计一个算法,要求用最少的天平使用次数找出这枚假币。
算法设计思路
将硬币的数量除以3,分为4组,A、,B、C组和余数组,然后A组和B组相互测量
如果A比B重或者B比A重,则选出较轻的一组,则假币范围缩小到较轻的那一组;如果A、B组的重量是一样的,则假币范围缩小在C组和余数组,再将C与A或者B作比较,若C与A或B的重量一样,则假币范围缩小到余数组。
找到范围后,继续递归分组,直到需比较的硬币数为1或者2或者3时,1时直接输出,2和3时作比较即可。
算法实现的伪代码
算法coinCompare ( int[ ] cArr, int first, int end )
输入:硬币数组,首位下标,末位下标
输出:假币所在的下标
- if ( end – first == 0 )
- 输出下标
- if(end – first == 1)
- 比较之后输出下标
- if(end – first == 2)
- 比较之后输出下标
- if ( end – first > 3 )
- int each = 每段分割的长度
- a[2] b[2] c[2] yu[2] 分别储存每段的首尾
- big[ ] = coinAdd( a, b, c, yu, cArr, first, end )
- coinCompare( cArr , big[0] , big[1] )
算法coinAdd ( input a[ ], input b[ ], input c[ ] , input yu[ ] , input cArr[ ], input first , input end)
输入:a组,b组,c组,余数组,原数组,首位,末尾
输出:假币所在数组
S1: 将a组的数加起来
S2: 将b组的数加起来
S3: 将c组的数加起来
S4: 将yu组的数加起来
S5: 比较每个数组加起来的大小
S6: if( 数组加起来的大小比较小 )
S7: return 该数组
实现代码
import java.util.Random;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] cArr = new int[n];
Random r = new Random();
for (int i = 0; i < cArr.length; i++) { //赋初值0, 真币为0, 假币为1
cArr[i] = 0;
}
cArr[r.nextInt(cArr.length)] = 1; //加入假币
coinCompare(cArr, 0, n - 1); //查找假币
}
public static void coinCompare(int[] cArr, int first, int end) { //查找假币
if (end - first == 0) { //如果区间为1则直接输出
System.out.println(first);
} else if (end - first == 1) { //如果区间为2则比较,输出假币
if (cArr[first] > cArr[end])
System.out.println(first);
else
System.out.println(end);
} else if (end - first == 2) { //如果区间为3则比较,输出假币
if (cArr[first] == cArr[first + 1]) {
System.out.println(end);
} else if (cArr[first] > cArr[first + 1]) {
System.out.println(first);
} else
System.out.println(first + 1);
} else { //如果区间大于3,则进行分割
int each = (((end - first) + 1) - ((end - first) + 1) % 3) / 3; //取分割长度
int[] a = new int[2]; //a,b,c,yu数组分别记录每段区间的首尾
int[] b = new int[2];
int[] c = new int[2];
int[] yu = new int[2];
a[0] = first;
a[1] = first + each - 1;
b[0] = a[1] + 1;
b[1] = b[0] + each - 1;
c[0] = b[1] + 1;
c[1] = c[0] + each - 1;
if (((end - first) + 1) % 3 == 0) {
yu[0] = -1;
yu[1] = -1;
} else {
yu[0] = c[1] + 1;
yu[1] = end;
}
int[] big = coinAdd(a, b, c, yu, cArr, first, end); //查找出假币所在的区间
coinCompare(cArr, big[0], big[1]); //递归分割
}
}
public static int[] coinAdd(int[] a, int[] b, int[] c, int[] yu, int[] cArr, int first, int end) { //选出假币区间,将区间的数全部加起来,找出等于1的区间
int cA = 0, cB = 0, cC = 0, cYu = 0;
for (int i = a[0]; i < b[0]; i++) {
cA += cArr[i];
}
for (int i = b[0]; i < c[0]; i++) {
cB += cArr[i];
}
if (yu[0] != -1) {
for (int i = c[0]; i < yu[0]; i++) {
cC += cArr[i];
}
for (int i = yu[0]; i < end + 1; i++) {
cYu += cArr[i];
}
} else {
for (int i = c[0]; i < end + 1; i++) {
cC += cArr[i];
}
cYu = -1;
}
if (cA == cB) {
if (cA == cC) {
return yu;
} else {
return c;
}
}
if (cA > cB) {
return a;
} else {
return b;
}
}
}
写得不好,多请谅解!