關於DFS的一些初級練習

關於DFS的參數問題,什麼在變化就把什麼作爲參數

1、部分和問題
題目描述:
給定整數a1、a2、…an,判斷是否可以從中選出若干數,使它們的和恰好爲K。

輸入
首先,n和k,n表示數的個數,k表示數的和。 接着一行n個數。 (1<=n<=20,保證不超int範圍)
輸出
如果和恰好可以爲k,輸出“YES”,並按輸入順序依次輸出是由哪幾個數的和組成,否則“NO”
樣例輸入
4 13
1 2 4 7
樣例輸出
YES
2 4 7

#include<stdio.h>
int a[20];
int n,k;
bool dfs(int sum,int i)
{
	if(i == n) return sum == k;//如果前 n 項都計算過了,則返回sum是否與n相等 
	if(dfs(sum,i + 1)) return true;//不加上date[i]的情況 
	if(dfs(sum + date[i],i + 1)) return true;//加上date[i]的情況 
	return false;//否則返回false 
}
int main()
{
	scanf("%d",&n);
	for(int i = 0;i < n;i ++)
		scanf("%d",&date[i]);
	scanf("%d",&k);
	if(dfs(0,0)) printf("yes");//判斷 
	else printf("no");
	return 0;
 } 

2、連通塊(Lake Counting POJ.NO2386)
題目簡單描述:
有一個大小爲 N * M 的園子,雨後激起了水。八連通的積水被認爲使鏈接在一起的。請求出園子裏總共有多少積水?
(八連通如下)
* * *
* w *
* * *

#include<stdio.h>
#include<iostream>

using namespace std;

int n,m;
char field[100][100];
void dfs(int x,int y)
{
	int dx,dy;
	field[x][y] = '.';//把搜到 w 的位置變成 . 
	//循環遍歷移動的8個方向 
	for(dx = -1;dx <= 1;dx ++)
		for(dy = -1;dy <= 1;dy ++){
			//向x方向移動dx,向y方向移動dy,結果座標爲(nx,ny) 
			int nx =x + dx;
			int ny =y + dy;
			
//		for(int nx = x - 1;nx <= x + 1;nx ++)
//		for(int ny = y - 1;ny <= y + 1;ny ++){  ----> 遍歷8個方向也可以這樣寫 
			//判斷(nx,ny)是否在園子裏,是否是 w 
			if(nx >= 0 && nx < n && ny >= 0 && ny < m && field[nx][ny] == 'w')
			dfs(nx,ny);
	}
	return;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i = 0;i < n;i ++)
		for(int j = 0;j < m;j ++)
			cin >> field[i][j];  //輸入 
	int sum = 0;
	for(int i = 0;i < n;i ++)
		for(int j = 0;j < m;j ++)
			if(field[i][j] == 'w'){//從有 w 的地方開始深搜 
				dfs(i,j);
				sum ++;//搜完一次sum加1 
			}
	printf("%d",sum);
	return 0; 
}

3、全排列

#include<stdio.h>
int n;
void dfs(char*date,int num)
{
	int i,j;
	if(date[num] == '\0'){
		printf("%s\n",date);
		return;
	}
	for(i = num;date[i];i ++){
	//換位
		char t = date[i];
		date[i] = date[num];
		date[num] = t;
		
		dfs(date,num+1);
		//回溯
		t = date[i];
		date[i] = date[num];
		date[num] = t;
	}
}
int main()
{
	char date[100];
	scanf("%d",&n);
	scanf("%s",date);
	printf("\n");
	dfs(date,0);
	return 0;
 } 

4、8-queens(皇后) 問題 ---->( 可以把 8 queens裏的8 改成 n.就時n queens的問題了)
思路:第一種是嘗試維護一個8*8的二維矩陣,每次找到一個空位放下一個皇后就把對應行列對角線上的棋格做個標記,如果某行找不到可放皇后的格子就把上一個皇后拿走並把對應行列對角線的標記取消掉;第二種方法直接放棄構造矩陣來假裝棋盤,我們把問題更加抽象化,八個皇后能放下一定是一行放一個,我們只需一個數組記錄每個皇后的列數(默認第N個放第N行),那麼問題就被抽象成了數組的第N個數和前N-1個數不存在幾個和差關係即可(比如差不爲零代表不在同一列)。
法一:(第一種思路)

#include<stdio.h>
int a[8][8];//棋盤 
int sum = 0;//記數 
bool check(int x,int y)//檢測(x,y)位置是否能方queen 
{
	int nx,ny;
	for(nx = 0;nx < 8;nx ++)
		if(a[nx][y] == 1) return false;//檢測 y 這一列上有無放過皇后的標誌 
	
	for(nx = x - 1,ny = y - 1;nx >=0 && ny >=0;nx --,ny --) 
		if(a[nx][ny] == 1) return false;//檢測(x,y)以上棋盤的部分的左對角線有無放過皇后的標誌 
	
	for(nx = x - 1,ny = y + 1;nx >=0 && ny < 8;nx --,ny ++)
		if(a[nx][ny] == 1) return false;//檢測(x,y)以上棋盤的部分的右對角線有無放過桓侯的標誌
		
	//對於行不用檢測,因爲每一行就只放一個皇后	 
	return true;
}
void dfs(int x)
{
	if(x > 7){
		sum ++;
		return;
	}
	for(int y = 0;y < 8;y ++){
		if(check(x,y)){
			a[x][y] = 1;//第x行 第y列 放上皇后,並標記 
			dfs(x + 1);//遞歸-->下一行 
			
			a[x][y] = 0;//回溯 
		}
	}
	return;
}
int main()
{
	dfs(0);
	printf("%d",sum);
	return 0;
 } 

法二:(第二種思路)

#include<stdio.h>
#include<math.h>
int a[100];
int sum = 0;
bool check(int*a,int x)
{
	for(int i = 1;i < x;i ++)
		if(a[i] == a[x] || abs(a[x] - a[i]) == abs (x - i))
			return false;
	return true;
}
void dfs(int x)
{
	if(x > 8){
		sum ++;
		return;
	}
	for(int y = 1;y <= 8;y ++){
		a[x] = y;
		if(check(a,x))
			dfs(x + 1);
	}
	return;
}
int main()
{
	dfs(1);
	printf("%d",sum);
	return 0;
}

5、2n-queens 問題
思路:根據 n-queens 問題---->兩個dfs。先放置黑皇后(白皇后),再放置白皇后(黑皇后)。當白皇后(黑皇后)放置完畢後,記數加 1

#include<stdio.h>
int a[50][50];
int sum = 0;
int n;
bool check(int x,int y,int s)
{
	int nx,ny;
	for(nx = 0;nx  < n;nx ++)
		if(a[nx][y] == s) return false;
	for(nx = x - 1,ny = y - 1;nx >= 0,ny >= 0;nx --,ny --)
		if(a[nx][ny] == s) return false;
	for(nx = x - 1,ny = y + 1;nx >= 0,ny < n;nx --,ny ++)
		if(a[nx][ny] == s) return false;
	
	return true;
}//檢查 
void dfs2(int x)
{
	int y;
	if(x == n){
		sum ++;
		return;
	} //當白皇后放置完畢,就找到一種放置方法 
	for(y = 0;y < n;y ++){
		if(a[x][y] == 1 && check(x,y,3)){
			a[x][y] = 3; //在放置白皇后的位置上做上標記 
			
			dfs2(x + 1);
			
			a[x][y] = 1;//回溯 
		}
	}
	return;
}
void dfs1(int x)
{
	int y;
	if(x == n) dfs2(0); //當黑皇后放置完畢,放置白皇后 
	else{
		for(y = 0;y < n;y ++){
			if(a[x][y] == 1 && check(x,y,2)){
				a[x][y] = 2;//在放置黑皇后的位置上做上標記 
				
				dfs1(x + 1);
				
				a[x][y] = 1;//回溯 
			}
		}
	}
	return;
}
int main()
{
	int i,j;
	scanf("%d",&n);
	for(i = 0;i < n;i ++){
		for(j = 0;j < n;j ++)
			scanf("%d",&a[i][j]);
		getchar();
	}//輸入哪些地方可以放置皇后,那些地方不可以放置皇后 
	dfs1(0); 
	printf("%d",sum);
	return 0;
 } 

6、方格填數
題目描述:
如下的10個格子
±-±-±-+
| | | |
±-±-±-±-+
| | | | |
±-±-±-±-+
| | | |
±-±-±-+

(如果顯示有問題,也可以參看【圖1.jpg】)

填入0~9的數字。要求:連續的兩個數字不能相鄰。
(左右、上下、對角都算相鄰)

一共有多少種可能的填數方案?

(深搜+回溯)

#include<stdio.h>
#include<math.h>
int date[5][6];
int vis[10];
int sum = 0;
//檢查,如果8個方向上有一個使兩數相減==1,則返回false 
bool check(int i,int j)
{
	for(int nx = i - 1;nx <= i + 1;nx ++)
		for(int ny = j - 1;ny <= j + 1;ny ++)
			if(abs(date[nx][ny] - date[i][j]) == 1)
				return false;
	return true;
}
void dfs(int x,int y)
{
	if(x == 3 && y == 4){
		sum ++;
		return;
	}//到(3,4)時,停止 
	
	for(int i = 0;i < 10;i ++){
		if(vis[i] == 0){//如果這個數沒被用過 
			date[x][y] = i;
			if(!check(x,y)){
				date[x][y] = -10;
				continue;//終止此次循環,進入下一次循環 
			}
			vis[i] = 1;//標記 
			if(y == 4) dfs(x + 1,1);
			else dfs(x,y + 1);       //遞歸 :一行結束後則換下一行 
			//回溯 
			vis[i] = 0;
			date[x][y] = -10;
		}
	}
	return;
}
int main()
{
	//給數組 a 賦初值 
	for(int i = 0;i < 5;i ++)
		for(int j = 0;j < 6;j ++)
			date[i][j] = -10;
	//從 a[1][2]處開始 
	dfs(1,2);
	printf("%d",sum);//輸出填數的所有方案 
	return 0;
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章