搜索(bfs+dfs)
搜索
在我们了解了递归之后就可以拿来做一些事♂情♂,比如走迷宫问题,那么这个时候我们就要用到搜索算法。
情况一(找有几条出路):
我们平时走迷宫容易见到的策略是:走一条路走到底并放线做标记,如果碰到墙壁就把线收回到上一个路口。
这样就引出了dfs的思想:穷尽每一步出现的所有方式,直到找到解为止。
模板:
void dfs(int n){
if(n>=k){//满足条件或者到底了
.....(所要求的步骤)
return ;
}
for(int i=1;i<=m;i++){
if(a[i]!=0(如果没有被标记到)&&(其他条件)){
a[i]=1;//做标记(放线)
dfs(n+1)//可以是更多的数据,比如总和的继承,或者是层数加进去等。
a[i]=0;//回归上一个状态
}
}
}
题目:洛谷P1219
#include<iostream>
using namespace std;
int z[300],d1[300],d2[300],b[300];
int n,ans=0;
int k=0;
void dfs(int x)
{
if(x>n)
{
ans++;
if(k<3)
{
for(int i=1;i<=n;i++)
cout<<b[i]<<" ";
cout<<endl;
}
k++;
return;
}
else
for(int i=1;i<=n;i++)
{
if((!z[i])&&(!d1[i+x])&&(!d2[x-i+n]))
{
b[x]=i;
z[i]=1;
d1[i+x]=1;
d2[x-i+n]=1;
dfs(x+1);
z[i]=0;
d1[i+x]=0;
d2[x-i+n]=0;
}
}
}
int main()
{
cin>>n;
dfs(1);
cout<<ans;
return 0;
}
记忆化
但是问题来了,如果是问去迷宫的最短路,还要在走一次,那么我们就又会花时间去找这条路走了多少步(或者有多少情况)那么这个时候就可以考虑记忆化(记忆化搜索),也就是把每一次走过的点记下来,然后下次再遇到的时候直接返还之前找过的数值。
模板:
int dfs(int n){
if(step[n]!=inf){//满足条件或者到底了
return step[n];//返回已经储存的条件
}
for(int i=1;i<=m;i++){
if(其他条件){
temp=dfs(n+1)+1;//记录到这个位置会有几步(记忆)
step[n]=min(temp,step[n]);//记录最短路径。
}
}
return step[n];//在最后一个地方需要返回一次,是对第一次做记忆的铺垫
}
题目:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll rpt[25][25][25];
ll w(ll a,ll b,ll c)
{
if(a<=0||b<=0||c<=0) return 1;
else if(rpt[a][b][c]!=0) return rpt[a][b][c];//如果有这个结果了,那么直接就返回数值。
else if(a>20||b>20||c>20) rpt[a][b][c]=w(20,20,20);
else if(a<b&&b<c) rpt[a][b][c]=w(a,b,c-1)+w(a,b-1,c-1)-w(a,b-1,c);
else rpt[a][b][c]=w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1);
return rpt[a][b][c];
}
int main()
{
ll a,b,c;
while(cin>>a>>b>>c){
memset(rpt,0,sizeof(rpt));
if(a==-1&&b==-1&&c==-1) break;
printf("w(%lld, %lld, %lld) = ",a,b,c);
if(a>20) a=21;
if(b>20) b=21;
if(c>20) c=21;
cout<<w(a,b,c)<<endl;
}
return 0;
}
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#define ll long long
using namespace std;
int x[10]={0,0,1,-1,0};
int y[10]={0,1,0,0,-1};
int map[1001][1001];
int step[1001][1001];
int sum,ans=-1;
int m,n;
int dfs(int xx,int yy){
if(step[xx][yy]!=0){
return step[xx][yy];
}
for(int i=1;i<=4;i++){
if(map[xx+x[i]][yy+y[i]]<map[xx][yy]&&xx+x[i]>0&&yy+y[i]>0&&xx+x[i]<=n&&yy+y[i]<=m){
int temp=dfs(xx+x[i],yy+y[i])+1;
step[xx][yy]=max(temp,step[xx][yy]);
}
}
return step[xx][yy];
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
cin>>map[i][j];
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++){
step[i][j]=dfs(i,j);
ans=max(step[i][j],ans);
}
// for(int i=1;i<=m;i++){
// for(int j=1;j<=m;j++){
// cout<<step[i][j]<<"\t";
// }
// cout<<'\n';
// }
cout<<ans+1<<'\n';
}
情况二(找最短出路):
这里就好像每次你走到岔路口都会分一次身,知道你找到出口的时候就回收分身。
我们要怎么样实现分身呢?这里我们会用到一种数据结构:队列。
队列有先进先出的性质,就好像一份访问名单:
| 队列长度 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| 方位 | 路口1(上) | 路口1(下) | 路口3(左) | 路口4(右) | 路口2(路口1走上)(上) | 路口3(路口1走下)(下) | 路口4(路口1箱左) |
.......一直到最后找到出口
由上表会发现,也就是把每个路口可以的情况都放入队列。
通过q.push()加入新的要访问的名单,当我们调查完这个路口时q.pop()来删除访问名单(队列)里的元素。
代码:
void bfs( ){
queue<结构体>q;
q.push(mp[start][start]);
while(!q.empty()){
now.x=q.x;
now.y=q.y;
if(step[endx][endy]!=0){
ans=step[endx][endy];//到终点了,结束
return;
}
for(int i=1;i<=x;i++){
next.x=now.x+q.x;
next.y=now.y+q.y;
if(next.x<=边界&&next.y<=边界&&a[next.x][next.y]==0&&mp[next.x][next.y]!=)//a用来判断有没有走过
q.push(next);
step[next.x][next.y]=step[now.x][now.y]+1;//刷新步数
}
q.pop();//清除已经访问过的名单
}
}
题目:
代码:
#include<iostream>
#include<queue>
#include<cstdio>
using namespace std;
queue<int>x;
queue<int>y;
int kuan,chang,sx,sy,nx,ny;
int fg[401][401];
int fx[9]={2,-2,2,-2,1,-1,1,-1};
int fy[9]={1,1,-1,-1,2,2,-2,-2};
int main()
{
int step=0;
cin>>chang>>kuan>>sx>>sy;
nx=sx;ny=sy;
for(int i=1;i<=chang;i++)
{
for(int j=1;j<=kuan;j++)
fg[i][j]=-1;
}
x.push(sx);y.push(sy);
fg[sx][sy]=step;
step++;
while(!x.empty()&&!y.empty())
{
for(int i=0;i<=7;i++)
{
nx=x.front() + fx[i];ny=y.front() + fy[i];//表示从开头开始查找,从某个位置开始
if(fg[nx][ny]==-1&&nx<=chang&&ny<=kuan&&nx>=1&&ny>=1)
{
x.push(nx);y.push(ny);//下一个位置
fg[nx][ny]=step;//步伐
}
}
x.pop();y.pop();//判断完了,弹出数据
step=fg[x.front()][y.front()]+1;//这个到地方然后走下一步
}
for(int i=1;i<=chang;++i)
{
for(int j=1;j<=kuan;++j)
printf("%-5d", fg[i][j]);
cout<<endl;
}
}
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
#define ll long long
int n,z,q,step=0,ans=0,zz,qq;
ll x[10]={0,1,-1,0,0};
ll y[10]={0,0,0,1,-1};
int s[1001][1001];
int a[1001][1001];
int b[1001][1001];
int time[1001][1001];
struct node{
int x,y;
};
node now,zd;
queue <node> h;
int main(){
int n,sj=1;
cin>>n;
for(int i=1;i<=305;i++){
for(int j=1;i<=305;i++)
a[i][j]=-1;
}
for(int i=1;i<=n;i++){
int xx,yy,t;
cin>>xx>>yy>>t;
for (int j=0;j<5;j++){
if (xx+x[j]>=0&&yy+y[j]>=0&&(a[xx+x[j]][yy+y[j]]==-1||a[xx+x[j]][yy+y[j]]>t))
a[xx+x[j]][yy+y[j]]=t;
}
}
b[1][1]=0;
time[1][1]=1;
now.x=0,now.y=0;
h.push(now);
h.pop();
while(!h.empty()) {
now=h.front();
sj=time[now.x][now.y]+1;
if(a[now.x][now.y]==-1){
cout<<sj<<'\n';
return 0;
}
h.pop();
for(int i=1;i<=4;i++){
zd.x=now.x+x[i],zd.y=now.y+y[i];
if(zd.x>0&&zd.x<=300&&zd.x>0&&zd.y<=300&&b[zd.x][zd.y]==0&&sj<a[zd.x][zd.y]){
h.push(zd);
time[zd.x][zd.y]=sj;
b[zd.x][zd.y]=1;
}
}
}
cout<<"-1\n";
}

浙公网安备 33010602011771号