DFS和BFS

本文详细介绍了深度优先搜索(DFS)的原理、递归与栈实现方法,并通过实例展示了在全排列问题、集合元素组合、数字摆放和数字构成等场景下的应用。通过AC代码演示,读者可以理解如何用DFS解决不确定循环次数的问题并确保搜索效率。
摘要由CSDN通过智能技术生成

两种重要的思维方法,搜索方法O(∩_∩)O哈哈~

概念

DFS:深度优先搜索。可以用递归实现,也可以用栈实现。不一定是在图中,有很多DFS暴力搜索解决的问题。经常用来解决无法确定该写几层循环的问题。
要有一个可以被具体描述、存储的状态,状态可以作为递归函数的参数,如果难以传递后者传递的代价过高(比如是个数组),也可以设为全局变量,然后通过打可逆标记来改变状态。
对于每个状态,我们要知道它能达到的后续状态有哪些,通过递归来搜索每一个后续状态。
搜索完每一个状态之后,此时,我们需要清理搜索时打的标记,防止影响后续搜索。
当然也有结束的位置,我们要设置搜到某个位置就要返回,不能无限递归下去。

例题

1、全排列https://vjudge.net/contest/417427#problem/H

题目:

输入一个整数n(n <= 9),输出1、2、3、······、n这n个数的全排列(按照字典序输出)。

AC代码
#include<bits/stdc++.h>
using namespace std;

int n;
int vis[10]; 
vector<int> v(10);
//dfs到第k位
void dfs(int k){
	for(int i=1;i<=n;i++){
		if(vis[i]) continue;
		vis[i] = 1;
		v[k] = i;
		if(k == n){
			for(auto i : v)		if(i) cout<<i<<' ';
			cout<<'\n';
			vis[i] = 0;
			return;
		}
		else dfs(k+1);
		vis[i] = 0;
	} 
}

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

2、https://codeforces.com/gym/338640/problem/A

题目大意:

给定n、k、s要求集合满足集合的每个元素<=n,元素个数为k,元素之和为s,问满足条件的集合有多少。

思路:

集合有无序性,从小到大DFS。我们可以DFS(当前元素个数m,当前元素和sum,当前元素最大值low)。当m=k,sum=s时,答案加1。

AC代码
#include <bits/stdc++.h>
using namespace std;

int n,k,s;
int ans;

void dfs(int m,int sum,int low){
	if(m == k && sum == s){
		ans++;
		return;
	}
	for(int i=low;i<=n && i+sum <= s;i++){
		dfs(m+1, sum+i, i+1);
	}
}
int main() {
	while(1){
		ans=0;
		cin>>n>>k>>s;
		if(!n && !k && !s) break;
		dfs(0,0,1);
		cout<<ans<<endl;
	} 
	return 0;
}

3、https://codeforces.com/gym/330157/problem/B

题目大意:

用火柴摆数字,约定数字的位数和使用的火柴数,问最大能摆出的数字。

思路:

数字从大到小DFS,当前这位确定之后要满足:剩余位数res*2<=剩余火柴数<=剩余位数res*7。

另解

第一次做的时候我想的十分复杂,通过计算用火柴摆每个数字的“性价比”,进行构造。

AC代码
#include<bits/stdc++.h>
using namespace std;
int a[10]={6,2,5,5,4,5,6,3,7,6};
void solve(){
	int n,sum=0; string s;
	cin>>n>>s;
	for(int i=0;i<(int)s.length();i++){
		sum+=a[s[i]-'0'];
	}
	int res=n;
	for(int i=0;i<n;i++){
		for(int j=9;j>=0;j--){
			if((sum-a[j])>=(res-1)*2 && (sum-a[j])<=(res-1)*7){
				cout<<j;
				sum-=a[j];
				res--;
				break;
			}
		}
	}
}
int main(){
	int cas;
	cin>>cas;
	while(cas--){
		solve();
		cout<<'\n';
	} 
	return 0;
}
另解
#include <bits/stdc++.h>
using namespace std;
//策略:将火柴摆成‘9’放前面 
int a[10]={6,2,5,5,4,5,6,3,7,6}; 
int ans[10];
int main() {
	int t;
	cin>>t;
	while(t--){
		int n;
		cin>>n;
	
		string ss;
		cin>>ss;
		
		int sum=0;
		for(int i=0;i<n;i++){
			sum+=a[ss[i]-'0'];
		}
		int cc=6*n-sum;
		
		memset(ans,0,sizeof(ans));
		ans[9]=n;
		if(cc<0){
			ans[8]=abs(cc);
			ans[9]-=abs(cc);
		}
		else if(cc>0){
			ans[1]=cc/4;
			ans[9]-=ans[1];
			cc%=4;
			if(cc==1){
				    if(ans[1]==0){
				     ans[9]--;
				     ans[5]++;
				    }
					else if(ans[1]==1){
					   ans[1]--;
					   ans[9]--;
					   ans[7]=ans[4]=1; 
					}
					else if(ans[1]>=2){
						ans[9]--;
						ans[1]-=2;
						ans[7]=3;
					}
			}
			else if(cc==2){
				if(ans[1]==0){
					ans[4]++;
					ans[9]--;
				}
				else if(ans[1]>=1){
					ans[7]=2;
					ans[9]--;
					ans[1]--;
				}
			}
			else if(cc==3){
				ans[9]--;
				ans[7]=1;
			}
		}
		for(int i=9;i>=0;i--){
			for(int j=0;j<ans[i];j++){
				cout<<i;
			}
		}
		cout<<endl;
	}
	return 0;
}

4、https://codeforces.com/gym/341451/problem/E

题面:

请添加图片描述

请添加图片描述

题意:

有t个样例,每个样例包含一个n,问0~n有多少数字只由0,4,6,7,9组成。

思路:

dfs,如果当前位大于要求数字则往后的数位可以任意选取0,4,6,7,9,ans+=ppow[剩余位数];如果恰好等于要求的数字,则固定该位往后一位再重新做如上考虑。ppow[i]表示i位数有多少满足条件的数,显然,ppow[i]=ppow[i-1]*5.

AC代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long

ll ppow[20],ans;
int dig[] = {0,4,6,7,9};
int num[20];

void dfs(int x, int ok){
	if(!ok || !x){
		ans += ppow[x];
		return;
	}
	
	for(int i=0;i<5;i++){
		if(num[x] == dig[i]){
			dfs(x-1,1);
		} 
		if(num[x] > dig[i]){
			dfs(x-1,0);
		}
	}
}
void solve(){
	ll n;
	cin>>n;
	
	int p=0;
	while(n){
		num[++p]=n%10;
		n/=10;
	}
	
	dfs(p,1);
}
signed main(){
	int cas;
	cin>>cas;
	
	ppow[0]=1;
	for(int i=1;i<=19;i++) ppow[i]=ppow[i-1]*5;
	
	while(cas--){
		ans=0;
		solve();
		cout<<ans<<endl;
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

u小鬼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值