搜索(dfs,bfs,剪枝,双向搜索,启发式搜索)

本文介绍了深度优先搜索(DFS)和广度优先搜索(BFS)在解决经典问题如八皇后、电梯问题、迷宫问题和奶酪问题中的应用。同时强调了剪枝技术在优化搜索效率中的关键作用,通过实例展示了如何在小木棍问题中运用剪枝策略降低时间复杂度。此外,还提及了双向搜索在某些问题中的优势。这些案例揭示了搜索算法与剪枝在信息技术领域的广泛适用性和重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

只有学会了剪枝的搜索,才叫会搜索
可见我以前都不会搜索
 
 

DFS(深搜)

这里写一个深搜的板子,很抽象,要理解。因为实际题目可能更抽象。

type dfs(type x······)
{
    if()//设置递归边界
    {
    	//判断最优解
        ans=____
        return;
    }
    //到达搜索边界
    if() return;
    for()
    {
        if()
        {
        	//修改全局变量
            vis[]=1;
            //搜索子节点
            dfs(son of x);
            //撤销修改,回溯
            vis[]=0;
        }
    }
}

 
 
P1219 [USACO1.5]八皇后 Checker Challenge

八皇后问题,所有学dfs肯定会遇到的一个经典题目,主要的就是对于标记数组的使用

#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>

typedef unsigned long long ULL;
typedef long long LL;
typedef long L;

int n,total;
int a[100],b[100],c[100],d[100];

void present()
{
    if(total<=2)
    {
        for(int k=1;k<=n;++k)
        {
            printf("%d ",a[k]);
        }
        printf("\n");
    }
    ++total;
}

void queen(int i)
{
    if(i>n)
    {
        present();
        return ;
    }
    else
    {
        for(int j=1;j<=n;++j)
        {
            if( (!b[j])&&(!c[i+j])&&(!d[i-j+n]) )
            {
                a[i]=j;
                b[j]=1;
                c[i+j]=1;
                d[i-j+n]=1;
                queen(i+1);
                b[j]=0;
                c[i+j]=0;
                d[i-j+n]=0;
            }
        }
    }
}


int main()
{
    scanf("%d",&n);
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    memset(c,0,sizeof(c));
    memset(d,0,sizeof(d));
    total=0;
    queen(1);
    printf("%d",total);
    return 0;
}

 
 
P1135 奇怪的电梯

这道题递归结束边界,搜索终止条件十分明显,子节点搜索也很清楚,比较好

#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>

typedef unsigned long long ULL;
typedef long long LL;
typedef long L;
typedef double DB;

int n,a,b,ans;
int arr[300];
bool vis[300];

void dfs(int x,int step)
{
    if(x==b)
    {
        ans=min(ans,step);
        return ;
    }
    if(step > ans)
        return ;//减枝
    if( x<1 || x>n )
        return ;

    vis[x]=1;
    if( !vis[x+arr[x]] )
        dfs(x+arr[x],step+1);
    if( !vis[x-arr[x]] )
        dfs(x-arr[x],step+1);
    vis[x]=0;
}

int main()
{
    scanf("%d%d%d",&n,&a,&b);
    for(int i=1;i<=n;++i)
        scanf("%d",&arr[i]);
    ans=1e9;
    vis[a]=1;
    dfs(a,0);
    if(ans!=1e9)
        cout << ans;
    else
        cout << -1;
    return 0;
}

 
 
P1605 迷宫

dfs更新方案数

#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>

typedef unsigned long long ULL;
typedef long long LL;
typedef long L;
typedef double DB;

int n,m,t;
int sx,sy,ex,ey,ans;
int Map[6][6];
int vis[6][6];
int dx[]={0, 0,0,1,-1};
int dy[]={0,-1,1,0, 0};

void dfs(int x,int y)
{
    if(x==ex && y==ey)
    {
        ans++;
        return ;
    }
    else
    {
        for(int i=1;i<=4;++i)
        {
            int tx=x+dx[i],ty=y+dy[i];
            if( !vis[tx][ty] && Map[tx][ty] )
            {
                vis[tx][ty]=1;
                dfs(tx,ty);
                vis[tx][ty]=0;
            }
        }
    }
}

int main()
{
    scanf("%d%d%d",&n,&m,&t);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            Map[i][j]=1;
    scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
    vis[sx][sy]=1;
    for(int i=1;i<=t;++i)
    {
        int tx,ty;
        scanf("%d%d",&tx,&ty);
        Map[tx][ty]=0;
    }
    dfs(sx,sy);
    cout << ans;
    return 0;
}

 
 
P3958 [NOIP2017 提高组] 奶酪

主要是三维空间球体之间的几何情况,而且一开始准备打表建个查询图的,然后没想到每次算dist居然过了。结构体的重载 < < <号,其实也可以用cmp函数替代

#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>

typedef unsigned long long ULL;
typedef long long LL;
typedef long L;
typedef double DB;

const int maxn=1e3+10;

struct node
{
    double x,y,z;
    bool operator< (const node &B) const
    {
        return z<B.z;
    }
}p[maxn];

int t;
int n,h,r;
bool vis[maxn];
bool flag;

double dist(node p1,node p2)
{
    return sqrt( (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y) + (p1.z-p2.z)*(p1.z-p2.z) );
}

void dfs(node x,int pos)
{
    if( x.z+r >= h )
    {
        flag=true;
        return ;
    }
    vis[pos]=1;
    for(int i=pos;i<n;++i)
    {
        if(flag)
            return ;
        if( !vis[i] && dist(x,p[i])<=r*2 )
            dfs(p[i],i);
    }
    vis[pos]=0;
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        memset(vis,0,sizeof(vis));
        memset(p,0,sizeof(p));
        scanf("%d%d%d",&n,&h,&r);
        for(int i=0;i<n;++i)
            scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z);
        sort(p,p+n);
        flag=false;
        for(int i=0;i<n;++i)
        {
            if(p[i].z-r<=0)
                dfs(p[i],i);
        }
        if(flag)
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

 
 

BFS(广搜)

抽象板子

type bfs(type x)
{
    queue<type> q;
    q.push(x);
    while(!q.empty())
    {
        type tx=q.front();
        q.pop();
        for()
        {
            //一系列操作
        }
    }
}

 
 

P1443 马的遍历

bfs的板子题,非常的典型

#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>

typedef unsigned long long ULL;
typedef long long LL;
typedef long L;

const int maxn=400+10;
int n,m,x,y;
int vis[maxn][maxn];
int dx[]={0,-2,-2, 2, 2,-1,-1, 1, 1};
int dy[]={0,-1, 1,-1, 1,-2, 2,-2, 2};

queue<int> q;
void bfs(int sx,int sy)
{
    vis[sx][sy]=0;
    q.push(sx);
    q.push(sy);
    while(!q.empty())
    {
        int tx=q.front();
        q.pop();
        int ty=q.front();
        q.pop();
        for(int i=1;i<=8;++i)
        {
            if( (tx+dx[i])>0 && (tx+dx[i])<n+1 && (ty+dy[i])>0 && (ty+dy[i])<m+1 && vis[tx+dx[i]][ty+dy[i]]==-1 )
            {
                vis[tx+dx[i]][ty+dy[i]]=vis[tx][ty]+1;
                q.push(tx+dx[i]);
                q.push(ty+dy[i]);
            }
        }
    }
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&x,&y);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            vis[i][j]=-1;
        }
    }
    bfs(x,y);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
            printf("%-5d",vis[i][j]);
        printf("\n");
    }
    return 0;
}

 
 

UVA572 油田 Oil Deposits

bfs直接标记,一套连招带走 可以用来增强自己bfs的自信

#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>

typedef unsigned long long ULL;
typedef long long LL;
typedef long L;
typedef double DB;

int n,m;
char Map[200][200];
int ans[200][200];
int vis[200][200];

int dx[]={0,1,-1,0, 0,-1,-1, 1,1};
int dy[]={0,0, 0,1,-1,-1, 1,-1,1};

void bfs(int x,int y,int cnt)
{
    queue<int> q;
    q.push(x);
    q.push(y);
    vis[x][y]=0;
    while(!q.empty())
    {
        int tx=q.front();
        q.pop();
        int ty=q.front();
        q.pop();
        for(int i=1;i<=8;++i)
        {
            int rx,ry;
            rx=tx+dx[i];
            ry=ty+dy[i];
            if(vis[rx][ry])
            {
                q.push(rx);
                q.push(ry);
                vis[rx][ry]=0;
            }
        }
    }
}

int main()
{
    while( scanf("%d%d",&n,&m)!=EOF )
    {
        if( n==0 && m==0 )
            break;
        for(int i=1;i<=n;++i)
            scanf("%s",Map[i]+1);
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=m;++j)
            {
                if(Map[i][j]=='@')
                    vis[i][j]=1;
            }
        }
        int cnt=0;
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=m;++j)
            {
                if(vis[i][j])
                    bfs(i,j,++cnt);
            }
        }
        printf("%d\n",cnt);
    }
    return 0;
}

 
 

UVA11624 Fire!

这道题bfs标记时间,主要是火不止一个,队列的储存的更新,烧到某个地块的时间的更新,和那个可怜的joe奔跑的时间比较。

#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>

typedef unsigned long long ULL;
typedef long long LL;
typedef long L;
typedef double DB;

const int maxn=1e3+10;
int t;
int r,c,ans;
char Map[maxn][maxn];
bool vis[maxn][maxn],vis2[maxn][maxn];
int fire[maxn][maxn];
int joe[maxn][maxn];

int dx[]={0,1,-1,0, 0};
int dy[]={0,0, 0,1,-1};

void joebfs(int x,int y)
{
    queue<int> q;
    q.push(x);
    q.push(y);
    joe[x][y]=1;
    while(!q.empty())
    {
        int tx=q.front();
        q.pop();
        int ty=q.front();
        q.pop();
        if( tx==1 || tx==r || ty==1 || ty==c )
        {
            ans=min(ans,joe[tx][ty]);
            break;
        }
        for(int i=1;i<=4;++i)
        {
            int rx=tx+dx[i],ry=ty+dy[i];
            if( vis[rx][ry] && joe[tx][ty]+1<fire[rx][ry] && joe[tx][ty]+1<joe[rx][ry] )
            {
                joe[rx][ry]=joe[tx][ty]+1;
                q.push(rx);
                q.push(ry);
            }
        }
    }
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&r,&c);
        for(int i=1;i<=r;++i)
            scanf("%s",Map[i]+1);
        memset(vis,0,sizeof(vis));
//        memset(vis,0,sizeof(vis2));
        memset(fire,1,sizeof(fire));

        memset(joe,1,sizeof(joe));
        int sx,sy;
        queue<int> q;

        for(int i=1;i<=r;++i)
            for(int j=1;j<=c;++j)
            {
                if(Map[i][j]!='#')
                    vis[i][j]=1;
                if(Map[i][j]=='J')
                    sx=i,sy=j;
                if(Map[i][j]=='F')
                {
                    q.push(i);
                    q.push(j);
                    fire[i][j]=1;
                }
            }

        while(!q.empty())
        {
            int tx,ty;
            tx=q.front();
            q.pop();
            ty=q.front();
            q.pop();
            for(int i=1;i<=4;++i)
            {
                int rx=tx+dx[i],ry=ty+dy[i];
                if( vis[rx][ry] && fire[tx][ty]+1<fire[rx][ry] )
                {
                    fire[rx][ry]=fire[tx][ty]+1;
                    q.push(rx);
                    q.push(ry);
                }
            }
        }

        ans=1e9;
        joebfs(sx,sy);

        if(ans!=1e9)
            printf("%d\n",ans);
        else
            printf("IMPOSSIBLE\n");
    }
    return 0;
}

 
 

剪枝

现在,你看到了一个搜索里最nb的技巧。剪枝的意义在于能够让暴力搜索减少了复杂时间复杂度,让我们能够过检测点拿到分数 但是同时剪枝的代码转化,来自于情况的各种优化,如果你不能分析出优化在哪里,那你就剪枝不成功,你就不能ac。

 
 

P1120 小木棍 [数据加强版]

1、将所给的木棍从大到小排列,从大到小进行搜索,不然每次都要全搜一遍。
2、因为搜索的顺序,如果最后一次拼接失败,那么和最后一样长的木棍肯定也是失败的,所有不再尝试最后一次拼接失败的长度
3、如果在当前原始木棍中第一次尝试拼接木棍的递归分支就返回失败,剩下的都是等价情况,其他原始木棍情况也会失败
4、如果当前原始木棍恰好被拼好,而剩下的原始木棍拼接失败,那么当前分支就是失败的,已经不用继续搜下去了

这道题用了4个剪枝,就是4个优化,剪枝1,2比较容易理解,剪枝3、4有一定的难度,限定时间内不一定能想出来,而且剪枝3,4有着一定的联系性

听不明白的话,是我语文不行,我已经尽力了

这道题暴搜吸肼都过不了,主要是剪枝。

#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>

typedef unsigned long long ULL;
typedef long long LL;
typedef long L;
typedef double DB;

int n,cnt1;
int a[100],v[100],cnt,len;

bool cmp(int x,int y)
{
    return x>y;
}

bool dfs(int stick,int cab,int last)
{
    if( stick>cnt )
        return true;
    if( cab==len )
        return dfs(stick+1,0,1);
    int fail=0;
    for(int i=last;i<=cnt1;++i)
    {
        if( !v[i] && cab+a[i]<=len && fail!=a[i] )
        {
            v[i]=1;
            if( dfs(stick,cab+a[i],i+1) )
                return true;
            fail=a[i];
            v[i]=0;
            if( cab==0 || cab+a[i]==len )
                return false;
        }
    }
    return false;
}

int main()
{
    int temp;
    cnt1=1;
    int sum=0;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%d",&temp);
        if(temp<=50)
        {
            a[cnt1++]=temp;
            sum+=temp;
        }
    }

    sort(a+1,a+cnt1,cmp);
    cnt1--;

    for(len=a[1];len<=sum;++len)
    {
        if( sum%len )
            continue;
        cnt=sum/len;
        memset(v,0,sizeof(v));
        if(dfs(1,0,1))
            break;
    }
    printf("%d",len);
    return 0;
}

 
 

双向搜索

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值