其他分享
首页 > 其他分享> > [复习#2022-8-17]基础搜索(1)

[复习#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