深度优先搜索---DFS(普及题)

目录

深度优先搜索

1.部分和问题

2.选数问题(洛谷1036)

3.健康的荷斯坦奶牛 Healthy Holsteins

4.求细胞数量&&Lack Counting数联通块

5.求二叉树的先序排列


深度优先搜索

  • 从某个状态开始不断转移状态直到无法转移,然后往回走一步再继续转移到其他状态,一直这样直到找到解。
  • 对于图或者树就是从一个节点开始一直往下走,直到没有子节点再返回上一个节点,一直这样直到走过所有的点。(就是一条路走到黑,走不动了就往回撤一步走别的路)
  • 实现方式:1、递归DFS         2、栈
  • 准备:一个记录是否到访过的数组

1.部分和问题

#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1e5;
int  n,k,a[maxn];


bool DFS(int i,int sum)//对于前i项已经得到了sum,对于i项之后的再进行分支
//递归的开始就是(0,0),结束就是i==n时(即所有数都看了一遍),返回sum==k的bool
{
	if(i==n)
		return sum==k;
	if(DFS(i+1,sum))//看过a[i]但不加a[i]; 
		return true;
	if(DFS(i+1,sum+a[i]))//加a[i]; 
		return true;
		
	//无论加和不加a[i]都不能凑够 k 
	return false;
 } 
 
 int main()
 {
 	cin>>n>>k;
 	for(int i=0;i<n;i++)
 	{
 		cin>>a[i]; 
	 }
	 
	 if(DFS(0,0))
	 	cout<<"Yes"<<endl;
	else
		cout<<"NO"<<endl;
 }

2.选数问题(洛谷1036)

传送门:P1036 [NOIP2002 普及组] 选数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这个和部分和很像了,递归也是有选和不选两种情况,结果判断一下是不是素数就行,具体看代码吧。

#include <bits/stdc++.h>
const int maxn=777;
const int mod=1e9+7;
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
string middle,after;
int a[maxn],ans=0,n,k;
bool isPrime(int n) 
{
    if (n <= 3) {
        return n > 1;
    }
    // 只有6x-1和6x+1的数才有可能是质数
    if (n % 6 != 1 && n % 6 != 5) {
        return false;
    }
    // 判断这些数能否被小于sqrt(n)的奇数整除
    int sq=sqrt(n);
    for (int i = 5; i <= sq; i += 6) {
        if (n % i == 0 || n % (i + 2) == 0) {
            return false;
        }
    }
    return true;
}

void dfs(int m,int sum,int look)
//已经选择了m个数字,看过了look个数字,那m个数字和是sum 
{
	if(m==k&&look==n)//当sum可以去判断素数时 
	{
		if(isPrime(sum))
			ans++;
		return;
	}
	if(look<n) 
		dfs(m,sum,look+1),dfs(m+1,sum+a[look+1],look+1);
		//不加这个数字,加这个数字 
}

int main()
{
     
     cin>>n>>k;
     for(int i=1;i<=n;i++)
     {
     	cin>>a[i];
	 }
	 dfs(0,0,0);
	 cout<<ans;
	
}

3.健康的荷斯坦奶牛 Healthy Holsteins

传送门:https://www.luogu.com.cn/problem/P1460

其实和前两个题一样,对于每一种饲料有选和不选两种情况,向下递归就成了。递归结束的情况就是我们看过了所有的饲料,这时候无论能不能喂饱牛都要结束啦,不过此时判断一下方案会不会更优,更优就取代原方案。

思考的一个点是:用Copy数组记录选择的饲料状态,用ans数组记录最优的选择;

一个小解释:

关于:

Copy[m+1]=look;//将饲料加入方案
    
 dfs(m+1,look+1);//选择这种饲料

dfs(m,look+1);//不选这种饲料

第一行就是说要选上这种饲料,但实际上这句话对下面两个dfs都作用了,不过因为第二个dfs传递的是m,最后复制方案的时候,也不会复制第m+1个,所以就是没影响啦;

#include <bits/stdc++.h>
const int maxn=777;

using namespace std;
string middle,after;
int v[maxn],anss=20,n,k;
int eat[maxn][maxn];
int ans[maxn],Copy[maxn];
int n_vtm,n_eat;//维他命种数和饲料种数 

bool judge(int x)//看看这x包饲料能不能喂饱牛
{
	for(int i=1;i<=n_vtm;i++)//每一种维他命
	{
		int temp=0;//开始累加每一种饲料的第i种维他命 
		for(int j=1;j<=x;j++)
		{
			temp+=eat[Copy[j]][i];
		 } 
		 if(temp<v[i])//选出来的饲料吃了不能满足第i种维他命的需求,咱就及时止损赶紧往外走。
		 	return false; 
	 } 
	 
	 return true;//没有维他命吃不饱,说明方案可行 
 } 

void dfs(int m,int look)
//已经选择了m种饲料,看到了第look种 
{
	if(look>n_eat)//没有饲料可以看了,咱得年终总结分析一下就结束了 
	{
		if(judge(m)&&m<anss)//这种方案可以喂饱牛而且饲料数还少,这不得赶紧搬方案 
		{
			anss=m;
			for(int i=1;i<=m;i++)
			{
				ans[i]=Copy[i];//复制方案ing 
			 } 
			 //年终总结完了,好方案也抄了,跑路!
			 
		 }  
		 return;
	}
	Copy[m+1]=look;//选这种饲料吧 
	
	dfs(m+1,look+1);dfs(m,look+1);
	//不加这个饲料,加这个饲料 
}

int main()
{
     cin>>n_vtm;
	 for(int i=1;i<=n_vtm;i++)
	 	cin>>v[i];
	 	
	 cin>>n_eat;
	 
     for(int i=1;i<=n_eat;i++)
     	for(int j=1;j<=n_vtm;j++)
     		cin>>eat[i][j];
	 dfs(0,1);
	 
	 cout<<anss;
	 for(int i=1;i<=anss;i++)
	 	cout<<" "<<ans[i];
	
}

4.求细胞数量&&Lack Counting数联通块

第一个是第二个的简单版,都是在数连通块,只不过第一个只考虑上下左右四个,第二个考虑周围的八个。由于我先做到了第二题,所有第一题是改了改第二题的代码直接用的。

(1)传送门:P1451 求细胞数量 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include <iostream>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
const int M=105;
const int N=105;
string a[N];
int xx[4]={0,0,-1,1};
int yy[4]={-1,1,0,0};
int n,m;
void dfs(int x,int y)
{
	a[x][y]='0';//先替代了
	//for(int i=0;i<n;i++)
	//	cout<<a[i]<<endl;
	//cout<<endl;
	for(int i=0;i<4;i++)
	{
			if(x+xx[i]<0||x+xx[i]>n-1||y+yy[i]<0||y+yy[i]>m-1) 
				continue;
			if(a[x+xx[i]][y+yy[i]]!='0')
				dfs(x+xx[i],y+yy[i]);
	 } 
	 //return;//尽兴而归! 
}

int main()
{
	cin>>n>>m;
	for(int i=0;i<n;i++)
		cin>>a[i]; 
	 
	int ans=0;

	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			if(a[i][j]!='0')
			{
				//cout<<i<<" "<<j<<endl;
				dfs(i,j);
				ans++;
			}	
		}
	 }
	 cout<<ans; 
}

(2)传送门:P1596 [USACO10OCT]Lake Counting S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这个题意读的我是很迷茫,总之就是如果一块地方,它的边边全是干的,他就是一个水洼。

所以我们就找连接在一起的w们就行。

我看的书,用深搜就是从一个w开始,将邻接的w变成干的代替,有八个方向要考虑所以状态转移也有八种。如果遇不到w了,说明这一块周围都是干的了,即找到了一个连通块。

我们每次都从有w的地方开始深搜,深搜的次数就是水洼连通块的个数(因为已经确定为水洼的地方我们替换成干的了,所以可以保证)。

#include <iostream>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
const int M=105;
const int N=105;
char a[N][M];
int n,m;
void dfs(int x,int y)
{
	a[x][y]='.';//先替代了
	for(int i=-1;i<=1;i++)
	{
		for(int j=-1;j<=1;j++)
		{
			if(x+i<0||x+i>n-1||y+j<0||y+j>m-1)//出院子了都 
				continue;
			if(a[x+i][y+j]=='W')//有积水我们就得替代它啊 
				dfs(x+i,y+j);
		}
	 } 
	 return;//尽兴而归! 
}

int main()
{
	cin>>n>>m;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			cin>>a[i][j];
		}
	 } 
	 
	 int ans=0;
	 //从有w的地方 
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			if(a[i][j]=='W')
			{
				dfs(i,j);
				ans++;
			}	
		}
	 }
	 cout<<ans; 
}

5.求二叉树的先序排列

传送门:P1030 [NOIP2001 普及组] 求先序排列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

关于这个题,我在二叉树、二叉搜索树这篇里已经写过,再总结一下学到的和深搜有关的,就是找到分隔点(即根),把长串分割长两半(左子树和右子树),然后一直递归到只有一个字符,就找完了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序和三三总有一个能跑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值