9.6-遞歸(//n!//最大公約數//窮舉n位二進制數//四皇后問題//骨牌組合//踩氣球)

遞歸算法:直接或間接調用自身的算法。

遞歸函數:用函數自身給出定義的函數。

 

遞歸的優點:算法簡潔易於分析理解。

遞歸的缺點:分支較多的函數時間、空間複雜度太大。

 

例1:求n!

#include<iostream>
using namespace std;

int f(int n);

int main(){
	int m;
	cin>>m;
	cout<<f(m)<<endl;
	return 0;
}

int f(int n){    //遞歸部分
    if(n == 1)
		return n;
	else
		return n*f(n-1);
}

 

例2:求最大公約數(輾轉相除法)

#include<iostream>
using namespace std;

int gcd(int a, int b);

int main(){
	int x, y;
	cin>>x>>y;
	cout<<gcd(x, y)<<endl;
	return 0;
}

int gcd(int a, int b){
    if(b == 0)
		return a;
	else
		return gcd(b, a%b);
}

 

例3:窮舉n位二進制數

#include<iostream>
using namespace std;

void dfs(int n);
void output();

int arr[20];
int m;

int main(){
	cin>>m;
	dfs(0);
	return 0;
}

void dfs(int n){
    if(n == m)   //達到要求窮舉的位數,輸出當前數組中的數據
		output();
	else
		for(arr[n] = 0; arr[n] < 2; arr[n]++){   //依次在數組中儲存0或1
		    dfs(n+1);     //遞歸搜索下一位數字
		}
}

void output(){
    int i;
	for(i = 0; i < m; i++){
	    cout<<arr[i];
	}
	cout<<endl;
}

 

例4:四皇后問題

      要求每一皇后所在行,列,斜線 y=x, y=-x方向bu'n不能有其他皇后,無輸入,輸出四個皇后都可放置的可能的排列方式。

#include<iostream>
using namespace std;

void dfs(int n);
void output();
int cannot_do(int row, int col);

int arr[10];

int main(){
	dfs(0);
	for(int i = 0; i < 4; i++){  //數組初始化,以免影響結果 
		arr[i] = 9;
	} 
	return 0;
}

void dfs(int n){
    if(n == 4)   
		output();
	else
		for(arr[n] = 0; arr[n] < 4; arr[n]++){   //遍歷四列 
			if(!cannot_do(n, arr[n])){  //若在該列可放下一個皇后,遞歸搜索下一層 
			    dfs(n+1);
			}     
		}
}

int cannot_do(int row, int col){
    int i;
	for(i = 0; i < row; i++){  //若在之前任一皇后所在列或斜線上,均不可放新皇后 
	    if(arr[i] == col || ((row - i) == (col - arr[i]) || (i - row) == (col - arr[i]) && !(arr[i] == 9)))
			return 1;
	}
	return 0;
}

void output(){  //輸出符合條件的放置方法 
    int i;
	for(i = 0; i < 4; i++){
	    cout<<arr[i];
	}
	cout<<endl;
}

 

例5:骨牌組合

      由0-6組成00,01,.……,06,11,……,16,……,22,……,66,共28個數字組合,分別編號爲0,1,2,……,27.將這28塊骨牌排列爲7*8的矩陣,將矩陣輸入。要求輸出可能的骨牌組合情況。

#include<iostream>
using namespace std;

void dfs(int n);
void init();
void output();
int cando(int m, int dire);
int find(int a, int b);


int pre_map[10][10];       //原始輸入地圖
int arr_map[10][10];   //註冊地圖
bool map_used[60];
bool data_used[30];

int main(){
	init();
	dfs(0);
}

void dfs(int n){
	int x1, y1, x2, y2;
	if(n == 56)
		output();
	else{
		if(!map_used[n]){
            x1 = n/8; y1 = n%8;   //該數在地圖中所在行列
            map_used[n] = true;   //第n個數已被匹配
			for(int i = 0; i < 2; i ++){ //0爲向右搜索,1爲向下搜索
				if(cando(n, i)){//被匹配數未被使用且組合未被註冊
					if(i == 0){   //匹配的數在地圖中所在行列
					    x2 = x1; y2 = y1+1;    
					}
					else{
					    x2 = x1+1; y2 = y1;
					}
					map_used[x2*8 + y2] = true;   //被第n個數匹配
					int t = find(pre_map[x1][y1], pre_map[x2][y2]);  //找出這對數所在組合編號 
					arr_map[x1][y1] = t;  //在匹配地圖上註冊
					arr_map[x2][y2] = t;  //在匹配地圖上註冊
					data_used[t] = true;  //標記組合被使用
					dfs(n+1);
					map_used[x2*8 + y2] = false;
					data_used[t] = false;
				}
			}
			map_used[n] = false;
		}
		else{
			dfs(n+1);
		}
	}
}

int cando(int m, int dire){
	int x1, x2, y1, y2;
	x1 = m/8; y1 = m%8;
	if((x1 == 6 && dire == 1) || (y1 == 7 && dire ==0))
	    return 0;
	if(dire == 0){
	    x2 = x1; y2 = y1 + 1;
	}
	else{
	    x2 = x1 + 1; y2 = y1;
	}
	if(map_used[x2*8 + y2] == false && data_used[find(pre_map[x1][y1], pre_map[x2][y2])] == false)
		return 1;
	return 0;
}

void init(){
    int i, j;
	for(i = 0; i < 7; i++)
		for(j = 0; j < 8; j++){
		    cin>>pre_map[i][j];
		    arr_map[i][j] = 0;
		}
	for(i = 0; i < 56; i++)
	    map_used[i] = false;
	for(i = 0; i < 28; i++)
		data_used[i] = false;
}

int find(int a, int b){
    int c;
	if(a > b){
		c = a; a = b; b = c;
	}
	if(a == 0)
		return(b);
	else if(a == 1)
		return(b+6);
	else if(a == 2)
		return(b+11);
	else if(a == 3)
		return(b+15);
	else if(a == 4)
		return(b+18);
	else if(a == 5)
		return(b+20);
	else
		return(27);
}

void output(){
	int i, j;
	cout<<endl;
	for(i = 0; i < 7; i++){
		for(j = 0; j < 8; j++){
		    cout<<arr_map[i][j]<<" ";
		}
		cout<<endl;
	}
}

 

 

例6.踩氣球

描述:六一兒童節,小朋友們做踩氣球遊戲,氣球的編號是1~100,兩位小朋友各踩了一些氣球,要求他們報出自己所踩氣球的編號的乘積。現在需要你編一個程序來判斷他們的勝負,判斷的規則是這樣的:如果兩人都說了真話,數字大的人贏;如果兩人都說了假話,數字大的人贏;如果報小數字的人說的是真話而報大數字的人說謊,則報小數字的人贏(注意:只要所報的小數字是有可能的,即認爲此人說了真話)。

輸入:輸入爲兩個數字,0 0表示結束;

輸出:輸出爲獲勝的數字。

輸入樣例:

36 62 49 343 0 0

輸出樣例:

62 49

#include<iostream>
#include<string.h>
#include<math.h>
using namespace std;

int judge(int t);

int m, n, x, y;
int used[101];

int main(){
	while(cin >> n >> m && n){
	    memset(used, 0, sizeof(used));
		x = judge(m);
		y = judge(n);
		if(x == y){
		    cout << max(m,n) <<endl;
		}
		else{
		    cout << min(m,n) <<endl;
		}
	}
}

int judge(int t){
	if(t == 1) return 1;
	for(int i = 1; i <= 100; i++){
		if(t%i){
		    continue;
		}
		if(used[i]){
		    continue;
		}
		else{
		    used[i] = 1;
			if(judge(t/i)) return 1;
			used[i] = 0;
		}
	}
	return 0;
}

 

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