考研复试系列——第四节 深度优先搜索

19 篇文章 0 订阅
12 篇文章 5 订阅

考研复试系列——第四节 深度优先搜索

前言


深度优先搜索(DFS)算法在ACM中是经常出现的解决问题的算法,相比暴力搜索可以更快的解决问题。
深度优先搜索算法的题目从整体架构上是类似的,所以我们先给出一个大体的DFS的模板:

dfs:
	if(判断条件)  //在这里进行递归的退出
		return;
	for(遍历内容) //进行遍历,例如图中访问每一个满足某一条件的节点
	{
		if(判断是否访问过以及其他题中给出的条件)
		{
			进行访问条件的设置及其他操作
			dfs //递归调用
			访问条件的重新设置及其他操作
		}
	}


例题


下面来介绍几道DFS的题目来加深对DFS的理解。

题目一


N皇后问题 在N*N的棋盘上放置 N 个皇后,使其不能够相互攻击 ,即任意两个皇后不能在同一行,同一列,同一斜线上。

思考 :显然每一行肯定有且仅有一个皇后,对于第一行我们确定了一个皇后之后,再确定第二行的皇后,依次确定到第N行的皇后。假如前N-1个皇后都已经确定了,
我们在确定最后一个皇后N时,确定一个位置后进行判断,然后再回到上一步,重新确定第N个皇后,由此可以看出这个问题可以使用DFS解决。
按照上面的模板,我们可以得出核心的DFS代码为:

void dfs(int n)//参数n表示当前确定第n个皇后
{
	if(n > N) //当前要确定的皇后n大于皇后的总个数N时
	{
		judge();//当前所有皇后的位置都确定了,进行判断是否满足题中条件(不能互相攻击)
		return;//退出递归
	}
	int i;
	for(i=1;i<=N;i++) //对于每一行(即每一个皇后)都有N个位置,要依次遍历
	{
		if(position[i] == 0) //若该位置没有访问过
		{
			position[i] = 1;//标记该位置被访问过
			tag[n] = i;//记录第n行皇后的位置,因为在judge函数中比较满足条件与否时要用到
			dfs(n+1);//递归调用
			position[i] = 0;//重新标记该位置,因为回溯后可能再次用到该位置
		}
	}
}

接下来我们只需要确定judge函数就可以解决了。 如果使用 i , j 分别表示两个皇后(1<= i <= N , 1<= j <= N , i 不等于 j ) 
xi ,xj 分别表示这两个皇后在棋盘中列的位置 (1<= xi <= N , 1<= xj <= N ,x i 不等于 xj )  
上面i 不等于j 以及 xi 不等于 xj 已经将横向和纵向的情况排除 。 那如何表示不能在同一斜线上呢 ?如果两者在同一斜线上,则有
 | i - j | = | xi - xj |   所以有| i - j | 不等于 | xi - xj |  。前面代码中可以看出皇后的位置保存在tag一维数组中,tag [1 ] 为第一个皇后的位置 。
由此judge函数为:

void judge()
{
	int i,j; 
	for(i=1;i<=N;i++)  
		for(j=1;j<i;j++) 
			if(abs(j-i)==abs(tag[j]-tag[i]))
				return ;  
	count++; //记录种类
}

最终我们的程序如下:

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

int count = 0,N;
int  *position,*tag;


void judge()
{
	int i,j; 
	for(i=1;i<=N;i++)  
		for(j=1;j<i;j++) 
			if(abs(j-i)==abs(tag[j]-tag[i]))
				return ;  
	count++; //记录种类
}

void dfs(int n)//参数n表示当前确定第n个皇后
{
	if(n > N) //当前要确定的皇后n大于皇后的总个数N时
	{
		judge();//当前所有皇后的位置都确定了,进行判断是否满足题中条件(不能互相攻击)
		return;//退出递归
	}
	int i;
	for(i=1;i<=N;i++) //对于每一行(即每一个皇后)都有N个位置,要依次遍历
	{
		if(position[i] == 0) //若该位置没有访问过
		{
			position[i] = 1;//标记该位置被访问过
			tag[n] = i;//记录第n行皇后的位置,因为在judge函数中比较满足条件与否时要用到
			dfs(n+1);//递归调用
			position[i] = 0;//重新标记该位置,因为回溯后可能再次用到该位置
		}
	}
}

int main()
{
	int i;
	cin>>N;
	position = new int[N+1];
	tag = new int[N+1];
	
	for(i=0;i<=N;i++)
		position[i] = 0; 
		
	dfs(1);
	cout<<count<<endl;
	return 0;
} 

题目二


整数划分 。这道题目在我以前的一篇博文上有过记录 ,链接如下:

http://blog.csdn.net/cassiepython/article/details/49797623

题目三


 四色图问题

给定一个图,使用邻接矩阵表示,表示国家与国家的相邻关系,使用至多4种颜色进行着色,求总共有多少种着色方法。
相邻的国家不能使用同一种颜色。

#include<iostream>
using namespace std;

int map[5][5] = {
	0,1,1,0,0,
	1,0,1,0,0,
	1,1,0,0,0,
	0,0,0,0,1,
	0,0,0,1,0
};

int color[5]; //记录节点的着色情况
int count = 0;

void judge()
{
	int i,j;
	for(i=0;i<5;i++)
	{
		for(j=0;j<i;j++)
		{
			if(map[i][j] == 1 && color[i] == color[j])
				return;
		}
	}
	count++;
}

void dfs(int n,int cr)
{
	color[n] = cr;
	if( n == 4)
	{
		judge();
		return;
	}
	int i;
	for(i=0;i<4;i++)
		dfs(n+1,i);
}

int main()
{
	for(int i=0;i<4;i++)
		dfs(0,i);
	cout<<count<<endl;
	return 0;
}

题目四 

给定一个n,输出1—n的全排列。如给定n=3,则输出全排列123 132 213 231 312 321 。

思考:我们先确定第一位,然后再确定第二位,确定第三位后再回溯到上一步,更改第二位的数,由此我们可以使用DFS来解决这道问题。

#include<iostream>
using namespace std;

int n;
int out[100];//保存结果
int number[100];//标记是否使用

void dfs(int step)
{
	int i;
	if(step > n)
	{
		for(i=1;i<=n;i++)
			cout<<out[i];
		cout<<endl;
		return;
	}

	for(i=1;i<=n;i++)
	{
		if(number[i] == 0)
		{
			number[i] = 1;
			out[step] = i;
			dfs(step+1);
			number[i] = 0;
		}
	}
}

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

关于全排列的算法不止是使用DFS实现,还有很多种方法,想要进一步知道请参考我的另一篇博文:

http://blog.csdn.net/cassiepython/article/details/45767361

题目五

  微笑 微笑 微笑 +  微笑 微笑 微笑 =  微笑 微笑 微笑  给定1至9这9个数 ,要求将这9个数填入表情中,使等式成立 。

#include<iostream>
using namespace std;

int out[10];//保存结果
int number[10];//标记是否使用
int total = 0;//记录总共多少种可能

void dfs(int step)
{
	int i;
	if(step > 9)
	{
		//判断是否满足条件
		if(out[1]*100+out[2]*10+out[3]+out[4]*100+out[5]*10+out[6]
			== out[7]*100 + out[8]*10 + out[9] )
		{
			total++;
			cout<<out[1]<<out[2]<<out[3]<<"+"<<out[4]<<out[5]<<out[6]
				<<"="<<out[7]<<out[8]<<out[9]<<endl;
			return;
		}
	}

	for(i=1;i<=9;i++)
	{
		if(number[i] == 0)
		{
			number[i] = 1;
			out[step] = i;
			dfs(step+1);
			number[i] = 0;
		}
	}
}
int main()
{
	dfs(1);
	cout<<"总共的种类为:"<<total<<endl;
	return 0;
}

如果173 + 286 = 459 与 286+173 = 459算同一种的话 结果total除以2即可。







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值