[复习#2022-8-17]基础搜索(1)
作者:互联网
DFS
(来自OIWIKI)在搜索算法中,该词常常指利用递归函数方便地实现暴力枚举的算法
那我们可以尝试一下:
先写出递归:
int xxx(int x,......)
{
if(边界到了)
{
......
}
else return xxx(x+1,......);
}
再把else部分改成暴力
int xxx(int x,......)
{
if(边界到了)
{
......
}
for(int i=1;i<=方案数;i++)
{
if(满足条件)
{
......
xxx(x+1,......);
......
}
}
}
锵锵,上面的就是DFS的一般框架啦!
可能我讲的不是很清楚,看一下下面的例题吧
那接下来一起来练一下手吧!
P1596 [USACO10OCT]Lake Counting S
[普及-]
很简单的一道题,用DFS枚举八个方向能不能走,同时走过去后封死后路(防止二次统计此处)
#include<bits/stdc++.h>
using namespace std;
char m[101][101];//map-输入
int bj[101][101];//周围剩下多少水
int gx[]={1,1,1,0,0,-1,-1,-1};
int gy[]={1,0,-1,1,-1,-1,0,1};
//坐标加上gx,gy得到八个方向
int ans;//答案
void dfs(int x,int y)
{
if(bj[x][y]==0)//边界
{
m[x][y]='.';//封死回头的路
return;
}
bj[x][y]=0;
m[x][y]='.';//同上
for(int i=0;i<8;i++)
{
bj[x+gx[i]][y+gy[i]]--;//少了一块水
if(m[x+gx[i]][y+gy[i]]=='W')
{
dfs(x+gx[i],y+gy[i]);
}
}
}
int x,y;
int main()
{
cin>>x>>y;
for(int i=1;i<=x;i++)
{
for(int j=1;j<=y;j++)
{
cin>>m[i][j];
if(m[i][j]=='W')
{
//打标记(八个方向有多少水坑),方便dfs确定边界
for(int k=0;k<8;k++)
{
bj[i+gx[k]][j+gy[k]]++;
}
}
}
}
for(int i=1;i<=x;i++)
{
for(int j=1;j<=y;j++)
{
if(m[i][j]=='W')
{
ans++;
dfs(i,j);
}
}
}
cout<<ans;
}
很简单吧,只需要枚举八个方向即可
接下来比这题难一点
奇怪的电梯
[普及/提高-]
首先DFS直接做就行了
#include<bits/stdc++.h>
using namespace std;
int k[300],n,a,b;
int ans=0xffff;
//上下操作
void dfs(int i,int cnt)//当前楼层,步数
{
if(i>n || i<1)return;//超界了
if(i==b)//边界-到达
{
ans=cnt;//记录
return;
}
//因为for只有两种操作---UP/DOWN,所以把for拆成了两个if
if(i+k[i]<=n)
{
dfs(i+k[i],cnt+1);
}
if(i-k[i]>=1)
{
dfs(i-k[i],cnt+1);
}
}
int main()
{
cin>>n>>a>>b;
for(int i=1;i<=n;i++)cin>>k[i];
dfs(a,0);
if(ans==0xffff)ans=-1;
printf("%d",ans);
}
这绝对能AC了................等等,20分?
除了1,3测试点,其他全MLE了。
检查以下数组开的差不多,应该是dfs枚举太多次,越界了。
第二次提交
#include<bits/stdc++.h>
using namespace std;
int k[300],n,a,b;
int ans=0xffff;
//上下操作
void dfs(int i,int cnt)//当前楼层,步数
{
if(i>n || i<1)return;
if(cnt>ans)return; //[+]如果比上次结果还大就没必要搜了
if(i==b)//边界-到达
{
ans=cnt;//记录
return;
}
//因为for只有两种操作---UP/DOWN,所以把for拆成了两个if
if(i+k[i]<=n)
{
dfs(i+k[i],cnt+1);
}
if(i-k[i]>=1)
{
dfs(i-k[i],cnt+1);
}
}
int main()
{
cin>>n>>a>>b;
for(int i=1;i<=n;i++)cin>>k[i];
dfs(a,0);
if(ans==0xffff)ans=-1;
printf("%d",ans);
}
60分......测试点2,6,7,10 TLE
接下来介绍一种新的方法-记忆化搜索
记忆化搜索
思考一下:如果你从二层出发,兜兜绕绕20多次又回到了二层,然后再兜回去,周而复始,会浪费很多的时间,造成TLE。我们可以使用记忆化搜索的方法,最大程度上减少浪费时间的问题。
对于这道题,我们可以让DFS“记住”到过的楼层,若上/下操作会到达己经到过的楼层,就不去。
#include<bits/stdc++.h>
using namespace std;
int k[300],n,a,b,bj[300];
int ans=0xffff;
//上下操作
void dfs(int i,int cnt)//当前楼层,步数
{
if(i>n || i<1)return;
if(cnt>ans)return;
if(i==b)//边界-到达
{
ans=cnt;//记录
return;
}
//因为for只有两种操作---UP/DOWN,所以把for拆成了两个if
if(i+k[i]<=n && bj[i+k[i]]==0)
{
bj[i]=1;
dfs(i+k[i],cnt+1);
bj[i]=0;
}
if(i-k[i]>=1 && bj[i-k[i]]==0)
{
bj[i]=1;
dfs(i-k[i],cnt+1);
bj[i]=0;
}
}
int main()
{
cin>>n>>a>>b;
for(int i=1;i<=n;i++)cin>>k[i];
dfs(a,0);
if(ans==0xffff)ans=-1;
printf("%d",ans);
}
AC
相信你已经学会了上面讲的记忆化搜索的方法,让我们继续下一题吧。
P3956 [NOIP2017 普及组] 棋盘
[普及+/提高]
根据题意,这是一个四个方向,三种情况的迷宫问题
(相信大家都能明白,回头走是不可取的,我们就直接用记搜了)
#include<bits/stdc++.h>
using namespace std;
int m[101][101],bj[101][101]={0};
int gx[]={-1,0,0,1};
int gy[]={0,1,-1,0};
int im,in;
int ans=0xffffff;
void dfs(int x,int y,int cnt,bool s)//cnt (money) s (用过魔法吗)
{
if(x>im || x<1 || y>im || y<1)return;
if(cnt>ans)return;
if(x==im && y==im)
{
ans=cnt;
return;
}
for(int i=0;i<4;i++)
{
if(bj[x+gx[i]][y+gy[i]]==0 && x+gx[i]>0 && x+gx[i]<=im && y+gy[i]>0 && y+gy[i]<=im)
{
if(m[x+gx[i]][y+gy[i]]==-1 && s==false)//don't have color
{
//pay 2+0 to go there
bj[x][y]=1;
m[x+gx[i]][y+gy[i]]=m[x][y];
dfs(x+gx[i],y+gy[i],cnt+2,true);
bj[x][y]=0;
m[x+gx[i]][y+gy[i]]=-1;
}
else if(m[x+gx[i]][y+gy[i]]==m[x][y])//same color
{
//pay 0 to go there
bj[x][y]=1;
dfs(x+gx[i],y+gy[i],cnt,false);
bj[x][y]=0;
}
else if(m[x+gx[i]][y+gy[i]]==!m[x][y])//different color
{
//pay 1 to go there
bj[x][y]=1;
dfs(x+gx[i],y+gy[i],cnt+1,false);
bj[x][y]=0;
}
}
}
}
int main()
{
memset(m,-1,sizeof(m));
cin>>im>>in;
for(int i=1;i<=in;i++)
{
int x,y;
cin>>x>>y;
cin>>m[x][y];
}
dfs(1,1,0,false);
printf("%d",ans==0xffffff?-1:ans);
}
60分,5个TLE,3个RE
我们明确的知道这道题是我们用来练习DFS的,现在DFS TLE了,肯定是还能剪枝(优化)
(题外话:不过这道题真的不止DFS一种做法,BFS、DP、Dijkstra都可以,我们的DFS+记忆化优化其实就是SPFA)
#include<bits/stdc++.h>
using namespace std;
int m[101][101],bj[101][101]={0};
int ct[101][101];//记录最小步数,再优化
int gx[]={-1,0,0,1};
int gy[]={0,1,-1,0};
int im,in;
int ans=0xffffff;
void dfs(int x,int y,int cnt,bool s)//cnt (money) s (用过魔法吗)
{
if(x>im || x<1 || y>im || y<1)return;
if(cnt>ans)return;
if(ct[x][y]<=cnt)return;//再跑没意义了,如果你已经跑过这里,得到最短,又来了一个更长的,那结果只会更大
else ct[x][y]=cnt;
if(x==im && y==im)
{
ans=cnt;
return;
}
for(int i=0;i<4;i++)
{
if(bj[x+gx[i]][y+gy[i]]==0 && x+gx[i]>0 && x+gx[i]<=im && y+gy[i]>0 && y+gy[i]<=im)
{
if(m[x+gx[i]][y+gy[i]]==-1 && s==false)//don't have color
{
//pay 2+0 to go there
bj[x][y]=1;
m[x+gx[i]][y+gy[i]]=m[x][y];
dfs(x+gx[i],y+gy[i],cnt+2,true);
bj[x][y]=0;
m[x+gx[i]][y+gy[i]]=-1;
}
else if(m[x+gx[i]][y+gy[i]]==m[x][y])//same color
{
//pay 0 to go there
bj[x][y]=1;
dfs(x+gx[i],y+gy[i],cnt,false);
bj[x][y]=0;
}
else if(m[x+gx[i]][y+gy[i]]==!m[x][y])//different color
{
//pay 1 to go there
bj[x][y]=1;
dfs(x+gx[i],y+gy[i],cnt+1,false);
bj[x][y]=0;
}
}
}
}
int main()
{
memset(m,-1,sizeof(m));
cin>>im>>in;
for(int i=1;i<=im;i++)for(int j=1;j<=im;j++)ct[i][j]=0xffffff;
for(int i=1;i<=in;i++)
{
int x,y;
cin>>x>>y;
cin>>m[x][y];
}
dfs(1,1,0,false);
printf("%d",ans==0xffffff?-1:ans);
}
AC
标签:cnt,return,复习,17,int,dfs,2022,ans,101 来源: https://www.cnblogs.com/2021cjx-akioi/p/16595077.html