深度优先搜索DFS(洛谷)

本文介绍了深度优先搜索(DFS)在ACM竞赛题目中的应用,包括八皇后问题、单词接龙、单词方阵、迷宫问题、加分二叉树和虫食算等,通过实例解析解题思路和剪枝技巧,展示DFS在不同问题中的解决策略。
摘要由CSDN通过智能技术生成

ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443
P1219 八皇后
题目:https://www.luogu.org/problemnew/show/P1219
题意:N皇后问题求解个数,部分输出
解法:回溯搜索每一行放置在哪一列就行

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std; 

int n;
int a[20],ans=0;
bool use[20];
bool ok(int row, int col){
   
	fo(i,1,row-1){
   
		if(a[row-i]-col==i || col-a[row-i]==i) return 0;
	}
	return 1;
}
void PF(){
   
	fo(i,1,n){
   
		printf("%d%c",a[i],i==n?'\n':' ');
	}
}
void dfs(int row){
   
	if(row>n){
   
		ans++;
		if(ans<=3)PF();
		return;
	}
	fo(i,1,n){
   
		if(!use[i] && ok(row,i)){
   
			a[row]=i;
			use[i] = 1;
			dfs(row+1);
			use[i] = 0;
		}
	}
}
int main(){
   
	scanf("%d",&n);
	dfs(1);
	cout<<ans<<endl;
	return 0;
}

P1019 单词接龙
题目:https://www.luogu.org/problemnew/show/P1019
题意:给定n个字符串,若两个字符串有重复部分则可以把两个字符串拼起来,所有字符串能拼接出来的最长字符串的长度
解法:

题目说两个拼接之后,另外相邻的两部分不能存在包含关系
就是说如果存在包含关系,就不能标记为使用过,就是at和ation这种,题目没说的很清 楚,就是指两个单词合并并且包含不能标记为使用过
上面那句话其实是提供了一个剪枝的条件,当重复字符串长度达到左字符串或者右字符串长度是不选
(ps:不剪也可以过啦…因为枚举的时候一样枚举选和不选两种情况啦)
这道题可以不使用string拼接
先看一下这组数据
3
actti s1
iox s2
ttioxasssss s3
a
13
s1,s2,s3个字符串和s1,s3字符串拼起来的长度是一样的,都是13
我们可以"敏锐地"发现,当中间字符串s2被右串包含s3的时候(该串不能单独和右串拼 接,iox和ttioxasssss不能拼接)
我们是可以"忽略"中间这个字符串的,因为它对长度并没有贡献
当s1包含s2的情况也是,s2没有贡献
所以我们不用考虑真的把两个字符串拼起来
比如字符串 s1, s2, s3
拼接 s3 的时候
s1和s2能拼接的情况我们只需要考虑
s2 是否能与 s3拼接
s1 是否能与 s3拼接这两种情况
具体做法我们在每次dfs的时候,每次让新加入的串作为"原串"去比较,每次只需要递归长度就行了

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
string str[25];
int n,max_len;
int vis[25];
int check(string s1, string s2){
   
	int len = min(s1.length(), s2.length());
	int k = s1.length();
	fo(i,1,len){
    // 从小到大枚举长度i 
		bool f = 1;
		for(int j=0; j<i; j++){
    // 检查长度为i是否能匹配成功 
			if(s1[k-i+j] != s2[j]) f=0; // 出现不同 
		}
		if(f)return i; // 贪心选择最小重叠 
	}
	return 0;
}	 	
void dfs(string now, int len){
   
	max_len = max(max_len, len);
	fo(i,1,n){
   
		if(vis[i]==2)continue;
		int k = check(now,str[i]);
		if(k==len||k==str[i].length())continue; // 包含关系,可以剪枝,删了这一行也不会超时 
		if(k>0){
    // 如果有重叠的话,可以选也可以不选 
			vis[i]++;
			dfs(str[i], len+str[i].length()-k);
			vis[i]--;
		}
	} 
}
int main(){
   
	cin>>n;
	fo(i,1,n)cin>>str[i];
	char c;
	cin>>c;
	fo(i,1,n){
   
		if(str[i][0]==c){
   
			vis[i]++;
			dfs(str[i],str[i].length());
			vis[i]--;	
		}
	}
	cout<<max_len<<endl<<endl;
	return 0;
} 

P1101 单词方阵
题目:https://www.luogu.org/problemnew/show/P1101
题意:给一n×n的字母方阵,内可能蕴含多个“yizhong”单词。单词在方阵中是沿着同一方向连续摆放的。摆放可沿着 8 个方向的任一方向,同一单词摆放时不再改变方向,单词与单词之间可以交叉,因此有可能共用字母。输出时,将不是单词的字母用*代替,以突出显示单词。
解法:搜索方阵中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值