用减治法求解n枚硬币问题
有n枚硬币,其中有一枚是假币,已知真币与假币重量不同,给你一架天平, 要求设计出一个高效的算法,来检测出这枚假币。
package task3_1;
public final class CoinUtil {
private CoinUtil(){}
//使用right来保存正确的硬币重量,只有在剩下最后两个硬币的时候用得到
private static int right;
public static int Find(int[] arr,int a, int b){
//如果传入的下标相同,代表只剩一个数组
if (a==b){
return a;
}
//用结束下标减去开始下标加一获得传过来的数组长度
int len= b-a+1;
//把数组分成四份,计算前三份的长度
int size=(len-len%3)/3;
//如果传入的下标是相邻的两个数,则分成两份
if (len==2){
if (arr[a]==right){
return a;
}else return b;
}
//根据前三份数组对应的下标求数组的和
int sum1,sum2,sum3;
sum1=sum(arr,a,a+size-1);
sum2=sum(arr,a+size,a+2*size-1);
sum3=sum(arr,a+2*size,a+3*size-1);
//第一份数组的和以其他两份的大小都不一样,代表假币就在里面
if (sum1!=sum2&&sum1!=sum3){
//第一份数组有假币,表示其他两份都是真币,就可以求得真币的重量,right=数组总重量/数组长度
right=sum2/len;
return Find(arr,a,a+size-1);
//第二份数组的和以其他两份的大小都不一样,代表假币就在里面
} else if (sum2!=sum1&&sum2!=sum3) {
//第二份数组有假币,表示其他两份都是真币,就可以求得真币的重量,right=数组总重量/数组长度
right=sum1/len;
return Find(arr,a+size,a+2*size-1);
//第三份数组的和以其他两份的大小都不一样,代表假币就在里面
} else if (sum3!=sum1&&sum3!=sum2) {
//第三份数组有假币,表示其他两份都是真币,就可以求得真币的重量,right=数组总重量/数组长度
right=sum2/len;
return Find(arr,a+2*size,a+3*size-1);
//代表假币不在这三份数组里面而在没被划分进数组的那堆零散的部分里
}else{
//零散数组有假币,表示其他两份都是真币,就可以求得真币的重量,right=数组总重量/数组长度
right=sum1/len;
return Find(arr,a+3*size,a+len-1);
}
}
//获取数组下标a,b之间的和(包含a,b)
private static int sum(int[] arr,int a,int b){
int sum=0;
for (int i = a; i <= b; i++) {
sum=sum+arr[i];
}
return sum;
}
}