搜索算法的应用

搜索算法

我们在之前的章节介绍了暴力枚举策略,将所有可能的情况都枚举一遍以获得最优解,但是枚举全部元素的效率如同愚翁移山,无法应付数据范围稍大的情形。本章在暴力枚举的基础上介绍了搜索算法,包括深度优先搜索和广度优先搜索,从起点开始,逐渐扩大寻找范围,直到找到需要的答案为止。

严格来说,搜索算法也算是一种暴力枚举策略,但是其算法特性决定了效率比直接的枚举所有答案要高,因为搜索可以跳过一些无效状态,降低问题规模。在算法竞赛中,如果选手无法找到一种高效求解的方法(比如贪心、递推、动态规划、公式推导等),使用搜索也可以解决一些规模较小的情况;而有的任务就是必须使用搜索来完成,因此这是相当重要的策略。

dfs的题目

排列数字
本蒟蒻发布的题解:

#include <iostream>
using namespace std;
int a[10];
int st[20];
int n;
void dfs(int t)
{
	if(t==n)//先确定结束条件
	{
		for(int i=0;i<n;i++)
		{
			if(i<n-1) cout<<a[i]<<" ";
			else cout<<a[i]<<endl;
		}
		return;
	}
	for(int i=1;i<=n;i++)//在当前阶段枚举所有可能
	{
		if(st[i]) continue;//在当前分支,判断i位置有么有用过
		st[i]=1;//如果没用过,将i位置标记为1
		a[t]=i;//在当前阶段,要把i存储到t位置
		dfs(t+1);//再做一遍同样的操作
		st[i]=0;//回溯回来,要把当前清零
	}
}
int main()
{
	cin>>n;
	dfs(0);//第一个参数是0,所以n初始化成0
 	return 0;
}

组合方案

#include <iostream>
using namespace std;
int a[10];
int n,r;
void dfs(int t,int last)
{
	if(t==r)//先确定结束条件
	{
		for(int i=0;i<t;i++)
		{
			printf("%3d",a[i]);//"%3d"是如果前面不足3位,自动补空格
		}
		cout<<endl;
		return;
	}
	for(int i=last+1;i<=n;i++)//为了防止重复,所以从last+1开始
	{
		a[t]=i;
		dfs(t+1,i);
	}
}
int main()
{
	cin>>n>>r;
	dfs(0,0);
 	return 0;
}

自然数的拆分问题

#include <iostream>
using namespace std;
int a[15],n;
void dfs(int t,int last,int sum)
{
	if(sum==n)//先确定结束条件
	{
		for(int i=0;i<t;i++)
		{
			cout<<a[i];
			if(i<t-1) cout<<"+";//如果不是最后一位,就输出加号
			else cout<<endl;//否则输出空格
		}
		return ;
	}
	for(int i=last;i<n;i++)
	{
		if(sum+i>n) continue;
		a[t]=i;
		dfs(t+1,i,sum+i);
	}
}
int main()
{
	cin>>n;
	dfs(0,1,0);
	return 0;
}

选数

#include <iostream>
using namespace std;
int a[30];
int n,k,ans=0;//要求的是种类数,所以定义变量计数
bool check(int b)//判断指数的函数
{
	for(int i=2;i<=b-1;i++)
	{
		if(b%i==0) return 0;
	}
	return 1;
}
void dfs(int t,int last,int sum)
{
	if(t==k)//先确定结束条件
	{
		if(check(sum)) ans++;//如果满足条件,技术一次
		return ;
	}
	for(int i=last+1;i<=n;i++)
	{
		dfs(t+1,i,sum+a[i]);
	}
}
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	dfs(0,0,0);
	cout<<ans;//输出种类数
	return 0;
}

kkksc03考前临时抱佛脚

#include <iostream>
using namespace std;
int s[4],a[4][25];
int res;
void dfs(int i,int t,int l,int r)
{
	if(t==s[i])//先确定结束条件
	{
		res=min(res,max(l,r));//这要求每科所需时间的最大值在和每科之间打擂台
		return ;
	}
	dfs(i,t+1,l+a[i][t],r);//这里只有两种选择,一个是给右脑,一个是给左脑
	dfs(i,t+1,l,r+a[i][t]);
}
int main()
{
	for(int i=0;i<4;i++)
	{
		cin>>s[i];
	}
	for(int i=0;i<4;i++)
	{
		for(int j=0;j<s[i];j++)
		{
			cin>>a[i][j];
		}
	}
	int ans=0;
	for(int i=0;i<4;i++)
	{
		res=1e9;//初始一下res的值
		dfs(i,0,0,0);
		ans+=res;//求一个总和
	}
	cout<<ans;
	return 0;
}

八皇后

#include <iostream>
using namespace std;
int a[30],b[30],c[30],d[30],ans=0,n;
void dfs(int t)
{
	if(t==n)
	{
		ans++;
		if(ans<=3)
		{
			for(int i=0;i<n;i++) cout<<a[i]<<" ";
			cout<<endl;
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(b[i]||c[i-t+n]||d[i+t]) continue;
		a[t]=i;
		b[i]=c[i-t+n]=d[i+t]=1;
		dfs(t+1);
		b[i]=c[i-t+n]=d[i+t]=0;//恢复现场
	}
}
int main()
{
	cin>>n;
	dfs(0);
	cout<<ans;//输出方案数
	return 0;
}

PERKET

#include <iostream>
using namespace std;
int a[20],b[20],n,minx=1e9;//a数组和b数组分别代表酸度和苦度
void dfs(int t,int sum1,int sum2)//t是要进行枚举的食材,sum1和sum2分别保存总酸度和总苦度
{
	if(t==n)//如果枚举的食材到了n位置(注意,这里是从0开始枚举的),就开始打擂台
	{
		if(sum1!=1||sum2!=0) minx=min(minx,abs(sum1-sum2));//打擂台前还得先检查一下有没有选择食材,如果选择了,继续打擂台
		return;
	}
	dfs(t+1,sum1,sum2);//这一个dfs是枚举没有选择的
	dfs(t+1,sum1*a[t],sum2+b[t]);//这一个是枚举选择了的
}
int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>a[i]>>b[i];
	}
	dfs(0,1,0);//这里总酸度要初始化成1,因为如果初始化成0,接下来乘的积就都是0
	cout<<minx;
	return 0;
}

bfs的题目

Lake Counting S

#include <iostream>
#include <queue>
using namespace std;
typedef pair<int,int> PII;
char g[110][110];
int n,m;
int dx[]={-1,-1,-1,0,1,1,1,0};
int	dy[]={-1,0,1,1,1,0,-1,-1};
void bfs(int x,int y)
{
	queue<PII> q;
	q.push({x,y});
	g[x][y]='.';
	while(q.size()!=0)
	{
		PII p=q.front();q.pop();
		x=p.first,y=p.second;
		for(int i=0;i<8;i++)
		{
			int a=dx[i]+x,b=dy[i]+y;
			if(a<0||a>=n||b<0||b>=m) continue;
			if(g[a][b]=='.') continue;
			q.push({a,b});
			g[a][b]='.';
		}
	}	
}
int main()
{
	cin>>n>>m;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++) cin>>g[i][j];
	}
	int cnt=0;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			if(g[i][j]=='W')
			{
				cnt++;
				bfs(i,j);
			}
		}
	}
	cout<<cnt;
	return 0;
}

Corn Maze S

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
int n,m,dist[310][310];
char g[310][310];
int dx[]={-1,0,1,0};//定义偏移数组
int dy[]={0,-1,0,1};
void transfer(int &x,int &y)//用来做传送的数组
{
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(g[i][j]==g[x][y]&&(x!=i||y!=j))
			{
				x=i,y=j;
				return;
			}
		}
	}
}
int bfs(int x,int y)
{
	queue<PII> q;//定义一个PII类型的队列
	memset(dist,-1,sizeof dist);//将dist初始化成-1
	q.push({x,y});//把xy加到队列里
	dist[x][y]=0;
	while(q.size()!=0)//只要队列不空,就一直循环
	{
		PII p=q.front();q.pop();//先把队首元素取出,在把这个元素pop出来
		x=p.first,y=p.second;//first,second是pair的两个属性
		int d=dist[x][y];
		if(g[x][y]=='=') return d;///如果起点位置就是终点的话,答案就是d
		if(g[x][y]>='A'&&g[x][y]<='Z') transfer(x,y);//如果这个位置是大写字母,就进行传送
		for(int i=0;i<4;i++)
		{
			int a=x+dx[i],b=y+dy[i];//a和b是扩展的位置
			if(a<1||a>n||b<1||b>m) continue;//越界检查
			if(dist[a][b]!=-1||g[a][b]=='#') continue;//合法检查
			dist[a][b]=d+1;
			q.push({a,b});//把ab加进队列里
		}
	}
}
int main()
{
	int a=0,b=0;//用两个变量保存位置
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cin>>g[i][j];
			if(g[i][j]=='@')//如果输入的位置是起点
			{
				a=i,b=j;//就把位置用ab保存起来
			}
		}
	}
	cout<<bfs(a,b);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值