減治法在組合問題中的應用 ——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);
	}
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章