搜索算法
我们在之前的章节介绍了暴力枚举策略,将所有可能的情况都枚举一遍以获得最优解,但是枚举全部元素的效率如同愚翁移山,无法应付数据范围稍大的情形。本章在暴力枚举的基础上介绍了搜索算法,包括深度优先搜索和广度优先搜索,从起点开始,逐渐扩大寻找范围,直到找到需要的答案为止。
严格来说,搜索算法也算是一种暴力枚举策略,但是其算法特性决定了效率比直接的枚举所有答案要高,因为搜索可以跳过一些无效状态,降低问题规模。在算法竞赛中,如果选手无法找到一种高效求解的方法(比如贪心、递推、动态规划、公式推导等),使用搜索也可以解决一些规模较小的情况;而有的任务就是必须使用搜索来完成,因此这是相当重要的策略。
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;
}
#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;
}
#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的题目
#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;
}
#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;
}