实验题目
在8枚外观相同的硬币中,有一枚是假币,并且已知假币与真币的重量不同,但不知道假币与真币相比较轻还是较重。可以通过一架天平来任意比较两组硬币,设计一个高效的算法来检测这枚假币。
实验要求
1)设计减治算法实现8枚硬币问题;
2)设计实验程序,考察用减治技术设计的算法是否高效;
3)扩展算法,使之能处理n枚硬币中有一枚假币的问题。
实现提示
假设用一个数组B[n]表示硬币,元素B[i]中存放第i枚硬币的重量,其中n-1个元素的值都是相同的,只有一个元素与其他元素值不同,则当n=8时即代表8枚硬币问题。由于8枚硬币问题限制只允许使用天平比较轻重,所以,算法中只能出现元素相加和比较的语句。
算法讲解
本例使用的的算法为三分查找法,时间复杂度O(log3n),即下面的findFakeCoin这个函数。
步骤1:
将需要查找的硬币分成三组,分组规则为,平均分成三组(数量对三取余),余数为2则平均分配给前两组,余数为1则分配给第三组。注意硬币数量一开始一定是大于或等于三的,不然此题无意义。
步骤2
比较前两组的重量
1.如果相等,说明假币在第三组,递归进入步骤1(即对第三组进行三分法)
2.如果不相等,说明假币在前两组,递归进入步骤1(即将前两组合为一组,再次进行三分)
步骤3
比到最后(last-first=1时)会有两种情况
1.还剩3个,比较前两个,相等则第三个为假币,不相等则取一个与第三个比,不相等就是假币,反之另外一个是假币。
2.还剩2个,这种情况两个硬币一定有一个是假的,取一个和其他其他任意一个比较,不相等就是假币,否则另一个是假币。
源码
class Ncoins:{
public:
int n;//n要求大于3
int *coins;
int countFind;
public:
Ncoins() {}
void initWeight(int n) {
this->countFind = 0;
coins = new int[n];
for (int i = 0; i < n; i++) coins[i] = 5;
srand((int)time(0));
int r;
do {
r = rand() % 9 + 1;//产生范围为[1,9]的随机数。
} while (r == 5);
coins[rand() % n] = r;//随机改变某一枚硬币的重量
}
//打印所有硬币的重量
void show() {
cout << "随机硬币质量:";
for (int i = 0; i<n; i++) {
cout << coins[i] << " ";
}
cout << endl;
}
//求第first枚硬币到last的质量总和
int getSum(int first, int last) {
int sum = 0;
for (int i = first; i <= last; i++) {
sum += *(coins + i);
}
return sum;
}
//返回假币的位置
int findFakeCoin(int first, int last) {
int len = last - first + 1;//需要比较的硬币数量
int eachGroup = len / 3 + (len % 3) / 2;//进行比较的每组数量
int restGroup = len - 2 * eachGroup;//未有比较的数量,即第三组数量
int sum1 = getSum(first, first + eachGroup - 1);//第一组质量总和
int sum2 = getSum(first + eachGroup, first + 2 * eachGroup - 1);//第二组质量总和
if (sum1 != sum2) {
this->countFind++;
if (last - first == 1) {
this->countFind++;
if (coins[first] != coins[last + 1]) return first;
else return last;
}
else {
first = first;
last = first + 2 * eachGroup - 1;
findFakeCoin(first, last);
}
}
else {
this->countFind++;
if (restGroup == 1) return last;
else if (restGroup == 2) {
this->countFind++;
if (coins[last] != coins[last - 2]) return last;
else return last - 1;
}
else {
last = last;
first = last - restGroup + 1;
findFakeCoin(first, last);
}
}
}
//显示查找结果
void showResult(int first, int last) {
int i = findFakeCoin(first, last);
cout << "第" << i + 1 << "枚硬币为假币" << endl;
cout << "查找次数:" << countFind << endl;
cout << endl;
}
//执行函数,可以改成main函数
void excuteReduceConquer() {
cout << "求n枚硬币中的一枚假币的位置。\n\n请输入n(n>=3)的值:";
int len;
do {
cin >> len;
if (len<3) cout << "输入有误,请重新输入!!" << endl;
} while (len<3);
this->n = len;
initWeight(n);
show();
showResult(0, n - 1);
}
};