枚舉算法--熄燈問題

最近在學習一些基礎的算法,編程這種事情,只能慢慢摸索,自己多去鍛鍊纔會取得好的效果!!

熄燈問題

問題:對於給定的一個按鈕矩陣5*6,使得改變每個元素的狀態之後(同時改變一個狀態會使得它的周圍的狀態都會改變)全部的燈都爲熄滅狀態。(其中0代表是熄滅的,1代表是亮着的)

基本思路:

1,如果直接開始採用枚舉法,則共有2^5*6要列舉的,不妨選取一個局部狀態,如果確定了該局部狀態,後面的狀態會基本定下來,首先對第一列的元素進行改變,若要使第一列的燈全部熄滅,第二列只有唯一一種解法,同時,若使第二列全部熄滅,第三列也是唯一確定的……以此類推,最後一列的狀態改變使得倒數第二列全部熄滅,同時最後一列也全部熄滅,則由於第一列產生的改變是正確的,否則,重新選取一個局部狀態。所以只需枚舉局部狀態,就可以完成了,此時有2^5需要列舉。

2,在一個矩陣中,有三種情況,在角上(處理三盞燈)、邊上(處理四盞燈)、內部(處理五盞燈)時,當改變他們的狀態時對周邊所產生的改變是不一樣的,不妨在矩陣外面罩上一層,使得全部滿足在內部的情況,此時,矩陣爲6*8。多取的行列只需要設置爲0就可以了。

3,枚舉方式:將按鈕矩陣的第一行看做一個二進制數,通過實現++操作實現。(從左往右)

4,選擇兩個數組,一個放置初始情況,另一表則表示需要進行的操作(0代表不改變,1代表改變狀態)。分別爲puzzle[6][8],press[6][8]。

          對於給定的press的第一行取值,計算出press的其它行的值:

                     press[r+1][c]=(puzzle[r][c]+press[r][c-1]+press[r][c]+press[r][c+1]+press[r-1][c])%2,0<r<5,0<c<7;

5,程序實現代碼:

#include<iostream>
#include<stdlib.h>
using namespace std;

int puzzle[6][8];//初始值的按鈕矩陣
int press[6][8];//設置的按鈕矩陣
//驗證由局部狀態引起的變換是不是滿足條件的
bool guess(){
	int r, c;
	for (r = 1; r < 5; ++r)
		for (c = 1; c < 7; ++c)
			press[r + 1][c] = (puzzle[r][c] + press[r][c] + press[r - 1][c] + press[r][c - 1] + press[r][c + 1]) % 2;
	//確定了第一行之後,剩下的行數(不包括最後一行)的狀態是由puzzle數組和press數組共同掌握的。
	//上一行的亮暗是由下一行決定的(相同的列),但是上一行的狀態又是由上、左、右以及自己的值、是否被按下所決定的。因此上一行最終若爲0.則不改變,否則改變
	for (c = 1; c < 7; ++c)//判斷第五行的燈能否被其餘的press數組熄滅
		if ((press[5][c] + press[5][c + 1] + press[5][c - 1] + press[4][c]) % 2 != puzzle[5][c])
			//如果改變四個的狀態所得的結果與初始狀態一樣,則表明已經熄滅了所有的燈;若爲1時,改變奇數個狀態,則表明爲使得燈熄滅,此時不滿足條件1!=1,返回true;
			//改變偶數個狀態,則表明原來的燈不變,還是亮着的,返回false,繼續下一個局部狀態再進行驗證;若爲0則改變狀況剛好相反!
			return false;
	return true;//在這個前面加了一個else導致出錯!!
}
void enumerate(){
	int c;
	for (c = 1; c < 7; ++c)
		press[1][c] = 0;//賦值局部初始狀態
	//當局部狀態不滿足時,進行第二次猜測,模擬二進制加法方式實現枚舉,需要處理進位,此時從左往右加!只對第一行做改變
	while (guess() == false){
		press[1][1]++;
		c = 1;
		while (press[1][c]>1){
			press[1][c] = 0;
			c++;
			press[1][c]++;
		}
	}
	return;
}
int main(){
	int r, c, cases, i;
	cout << "input cases : ";
	cin >> cases;
	for (r = 0; r < 6; ++r){
		press[r][0] = press[r][7] = 0;
		puzzle[r][0] = puzzle[r][7] = 0;
	}

	for (c = 1; c < 7; ++c){
		press[0][c] = 0;
		puzzle[0][c] = 0;
	}

	//輸入數據
	for (i = 1; i <= cases; ++i){
		for (r = 1; r < 6; ++r){
			for (c = 1; c < 7; ++c)
				cin >> puzzle[r][c];
		}
		//}
		enumerate();
		//輸出數據
		//for (i = 0; i < cases; ++i){
		cout << "puzzle #" << i << endl;
		for (r = 1; r < 6; ++r){
			for (c = 1; c < 7; ++c)
				cout << press[r][c];
			cout << endl;
		}
	}
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章