专题·深度优先搜索(DFS)【including 2N皇后,等边三角形,中国邮递员问题

初见安~这篇我们来讲讲深搜(DFS)

前文我们讲过了递归【这里是递推递归】,这里我们就要运用到啦~

所谓深搜,也顾名思义就是在深度上搜索,到了尽头则返回上一层,换一条路继续搜——也就是递归思想。

先看一道题了解一下吧:【中国邮递员问题

对这道题就是很直白的dfs搜索目标。也正如题解里所言:很多dsf也可以用bfs实现,但与此同时也有很多是不能实现的。

比如搜索路径条数——

2N皇后

(本题出自计蒜客)

Description

给定一个 n*n 的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入 n 个黑皇后和 n 个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条斜线(包括正负斜线)上,任意的两个白皇后都不在同一行、同一列或同一条斜线(包括正负斜线)上。问总共有多少种放法?n 小于等于 8。

Input

输入的第一行为一个整数 n,表示棋盘的大小。接下来 n 行,每行 n 个 0 或 1 的整数,如果一个整数为1,表示对应的位置可以放皇后,如果一个整数为 0,表示对应的位置不可以放皇后。

Output

输出一个整数,表示总共有多少种放法。

Sample Input 1

4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1

Sample Output 1

2

Sample Input 2 

4
1 0 1 1
1 1 1 1
1 1 1 1
1 1 1 1

 

题解

了解题目后,我们可以有个大概的思路:黑白皇后之间无所谓冲突,所以我们只要分别处理好并标记位置确定同色皇后之间不冲突即可。那我们不妨大胆(不怕超时)尝试一下:先处理黑皇后(先白也可以),每放一个都四下检查一次有没有同色皇后;到了最后一行且皇后数量达到了2N时就开始找白皇后or跳出递归,已走过所有路径。

下面是代码及详解:

#include<bits/stdc++.h>
using namespace std;
int n,a[10][10],k=0;
int cnt=0;//cnt计数

bool in(int x,int y) //检查是否越界
{
	return 1<=x&&x<=n&&1<=y&&y<=n;
}

void dfs(int i,int q)//黑标记2白标记3 ,i为到第几行了。
{
	if(i>n) return;//越界
	
	for(int j=1;j<=n;j++)
	{
		if(in(i,j)&&a[i][j]==1)//在(i,j)位置四下搜索是否遇得到已放皇后
		{
			bool flag=1;
			for(int k=1;k<=i-1;k++)//第i行后面的确定是没放的,所以不必搜索
			{
				if(a[k][j]==q) flag=0;
			}
			//不用搜索同一行,因为放了就深入到下一行
			int	tx=i-1,ty=j+1;
			while(in(tx,ty))//斜向搜索
			{
				if(a[tx][ty]==q) flag=0;
				tx--;ty++;
			}
			
			tx=i-1,ty=j-1;
			while(in(tx,ty))
			{
				if(a[tx][ty]==q) flag=0;
				tx--;ty--;
			}
			
			
			if(flag)//通过了以上的检查操作
			{
				a[i][j]=q;
				if(i==n&&q==2)//黑皇后放完了
				{
					dfs(1,3);
				}
				else if(i==n&&q==3) //搜索完毕,计数
				{
					cnt++;
				}
				else dfs(i+1,q);
				a[i][j]=1;//递归循环,复原
			}
		}
	}
}

int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cin>>a[i][j];
	dfs(1,2); 
	cout<<cnt<<endl;
	return 0;
}

我知道还有个问题叫做8皇后问题。【和上述思路差不多,可以去试一下】


OK,我们再来看一道题:

等边三角形

(本题出自YCOJ)

Description

小白手上有一些小木棍,它们长短不一,小白想用这些木棍拼出一个等边三角形,并且每根木棍都要用到。 例如,小白手上有长度为 1,2,3,3 的4根木棍,他可以让长度为1,2 的木棍组成一条边,另外 2 跟分别组成 2 条边,拼成一个边长为 3 的等边三角形。小白希望你提前告诉他能不能拼出来,免得白费功夫。

Input

首先输入一个整数 n(3 ≤ n ≤ 200),表示木棍数量,接下来输入 n 根木棍的长度 p_i(1 ≤ p_i ≤ 10000)。

Output

如果小白能拼出等边三角形,输出"yes",否则输出"no"。

Sample Input 1 

5
1 2 3 4 5

Sample Output 1

yes

Sample Input 2 

4
1 1 1 1

Sample Output 2

no

题解

我们知道等边三角形就是三边相等。所以这道题我们可以凑(强行枚举?)出结果来。当然本题只需要返回是否存在解,所以找到了就可以了。

下面是代码及详解——

#include<bits/stdc++.h>
using namespace std;
int n,num[200],s=0;
bool flag=0;
void dfs(int a,int b,int c,int i)//三边长及用到了第几个木棍
{
	
	if(flag==1) return;//已经找到解了就不用在找了
	if(a==b&&b==c&&a!=0&&i==n+1)//如果找到了:
	{
		flag=1;
		return;
	}
	if(i>n+1||a>s||b>s||c>s)//返回上一个,因为长度超过了
	{
		return;
	}
	dfs(a+num[i],b,c,i+1);//强行凑!(什么鬼……)
	dfs(a,b+num[i],c,i+1);
	dfs(a,b,c+num[i],i+1);
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>num[i];
		s+=num[i];
	}

	if(s%3!=0)//如果连总长都不能三等分的话,就不用考虑了。
	{
		cout<<"no"<<endl;return 0;
	}

	else
	{
		s=s/3;//s变为要凑出来的边长度
		dfs(0,0,0,1);
	}
	if(flag==1) cout<<"yes"<<endl;//flag标记
	else cout<<"no"<<endl;
	return 0;
}

最后,其实DFS有一个优化操作——迭代加深搜索。什么是迭代加深呢?就是搜索前先限定好深度搜,如果没搜到那么我们再更改深度。相当于是枚举答案。

画个图理解一下:假设我们枚举的深度一点点加深,那么搜索路径就是这样的:

只有当搜索深度为3的时候最后一个点才会被搜到。

这样搜看起来前面的点被重复搜了很多次,但其实效率多数时候是比一路搜到最深处的dfs要优秀一些的。

有一个不那么经典的迭代加深搜索题目:洛谷P2329 栅栏(fence8)。虽然是二分枚举的答案,但是也算是迭代加深的。

后期还有不少搜索的经典题目,欢迎到博客目录翻阅~

迎评:)

——End——

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值