减治法在组合问题中的应用 ——8枚硬币问题

实验题目

在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);
	}
};
发布了18 篇原创文章 · 获赞 16 · 访问量 1418

猜你喜欢

转载自blog.csdn.net/weixin_44155115/article/details/103838171