ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[复习#2022-8-17]基础搜索(1)

2022-08-17 18:33:52  阅读:96  来源: 互联网

标签:cnt return 复习 17 int dfs 2022 ans 101


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有