关于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;
 }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值