搜索(1)BFS基础

一、floodfill

(1)

 bfs,dfs均可。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;


typedef pair<int,int> PII;
const int N =1010;
int dx[8]={0,-1,-1,-1,0,1,1,1},dy[8]={-1,-1,0,1,1,1,0,-1};


char g[N][N];
int n,m;
bool st[N][N];
int ans;



void bfs()
{
    queue<PII> q;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            if(!st[i][j]&&g[i][j]=='W')
            {
                ans++;
                q.push({i,j});
                while(q.size())
                {
                    auto t=q.front();
                    q.pop();
                    for(int i=0;i<8;i++)
                    {
                        int x=t.first+dx[i],y=t.second+dy[i];
                        if(x>=0&&x<n&&y>=0&&y<m&&g[x][y]=='W')
                            if(!st[x][y]) st[x][y]=true,q.push({x,y});
                    }

                }
            }
            else continue;
        }

}


int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++) cin>>g[i];

    bfs();
    cout<<ans;
}

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4194842/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

(2)

 位运算来考虑墙。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

typedef pair<int,int> PII;
const int N =55;

int n,m;
int g[N][N];
bool st[N][N];
int cnt;

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


int bfs(int sx,int sy)
{
    queue<PII> q;
    int area=0;
    q.push({sx,sy});
    st[sx][sy]=true;

    while(q.size())
    {
        area++;
        auto t=q.front();
        q.pop();

        for(int i=0;i<4;i++)
        {
            int a=t.first+dx[i],b=t.second+dy[i];
            if(a<0||b<0||a>=n||b>=m) continue;
            if(st[a][b]) continue;
            if(g[t.first][t.second]>>i&1) continue;//这个方向门不开(要和dx dy配合)
            q.push({a,b});
            st[a][b]=true;
        }

    }
    return area;
}


int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            cin>>g[i][j];

    int area=0;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            if(!st[i][j])
            {
                area=max(area,bfs(i,j));
                cnt++;
            }
        }
    cout<<cnt<<endl;
    cout<<area<<endl;
}

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4200345/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

(3)

 每个点bfs一遍,bfs时如果碰到height一样的就加入队列,如果碰到height不一样的则判断正在搜索的是山峰还是山谷。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define x first
#define y second
using namespace std;

typedef pair<int,int> PII;
const int N = 1010;
int n;
int h[N][N];
bool st[N][N];


void bfs(int i,int j,bool &hash,bool &hasl)
{
    queue<PII> q;
    q.push({i,j});
    st[i][j]=true;

    while(q.size())
    {
        auto t=q.front();
        q.pop();

        for(int i=t.x-1;i<=t.x+1;i++)
            for(int j=t.y-1;j<=t.y+1;j++)
            {
                if(i==t.x&&j==t.y) continue;
                if(i<0||j<0||i>=n||j>=n) continue;
                if(h[i][j]!=h[t.x][t.y])
                {
                    if(h[i][j]>h[t.x][t.y])  hash=true;
                    else hasl=true;
                }
                else if(!st[i][j])
                {
                    st[i][j]=true;
                    q.push({i,j});
                }
            }
    }
}



int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>>h[i][j];

    int peak=0,valley=0;

    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        {
            if(!st[i][j])
            {
                bool hash=false,hasl=false;
                bfs(i,j,hash,hasl);
                if(!hash) peak++;
                if(!hasl) valley++;
            }
        }

    cout<<peak<<" "<<valley;

}

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4200594/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

二、最短路模型

(1)

 边权为1,bfs可找最短路。用pre数组存储某个点的前驱,则可获得最短路线

为方便,反向搜索最短路,然后可从0,0开始直接输出前驱,即为正向最短路

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define x first
#define y second
using namespace std;

typedef pair<int,int> PII;
const int N =1010,M=N*N;

int n;
int g[N][N];
PII pre[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

void bfs(int sx,int sy)
{
    queue<PII> q;
    q.push({sx,sy});
    memset(pre,-1,sizeof pre);
    pre[sx][sy]={0,0};

    while(q.size())
    {
        auto t=q.front();
        q.pop();

        for(int i=0;i<4;i++)
        {
            int a=t.x+dx[i],b=t.y+dy[i];
            if(a<0||b<0||a>=n||b>=n) continue;
            if(g[a][b]) continue;
            if(pre[a][b].x!=-1) continue;//被搜过

            q.push({a,b});
            pre[a][b]=t;
        }
    }

}


int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>>g[i][j];

    bfs(n-1,n-1);
    PII end(0,0);//构造函数

    while(true)
    {
        cout<<end.x<<" "<<end.y<<endl;
        if(end.x==n-1&&end.y==n-1) break;
        end=pre[end.x][end.y];
    }
    return 0;
}


作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4202184/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

(2)

 直接bfs搜,,计算一下跳跃之后坐标的数组即可

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define x first
#define y second
using namespace std;

typedef pair<int,int> PII;

const int N =155;

int n,m;
char g[N][N];
int dist[N][N];

int bfs()
{
    int dx[] = {-2, -1, 1, 2, 2, 1, -1, -2};
    int dy[] = {1, 2, 2, 1, -1, -2, -2, -1};

    int sx,sy;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            if(g[i][j]=='K') sx=i,sy=j;

    queue<PII> q;
    q.push({sx,sy});
    memset(dist,-1,sizeof dist);
    dist[sx][sy]=0;

    while(q.size())
    {
        auto t=q.front();
        q.pop();

        for(int i=0;i<8;i++)
        {
            int a=t.x+dx[i],b=t.y+dy[i];
            if(a<0||b<0||a>=n||b>=m) continue;
            if(g[a][b]=='*') continue;
            if(dist[a][b]!=-1) continue;
            if(g[a][b]=='H') return dist[t.x][t.y]+1;

            dist[a][b]=dist[t.x][t.y]+1;
            q.push({a,b});
        }
    }
}


int main()
{
    cin>>m>>n;
    for(int i=0;i<n;i++) cin>>g[i];

    cout<<bfs()<<endl;
}

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4202245/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

(3)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N =100010;
int n,k;
int d[N];

int bfs()
{
    memset(d,-1,sizeof d);
    d[n]=0;
    queue<int> q;
    q.push(n);
    while(q.size())
    {
        int t=q.front();
        q.pop();
        
        if(t==k)   return d[t];
        
        
        int x=t+1;
        if(x<=100000&&d[x]==-1)    
        {
            d[x]=d[t]+1;
            q.push(x);
        }
        x=t-1;
        if(x>0&&d[x]==-1)   
        {
            d[x]=d[t]+1;
            q.push(x);
        }
        x=2*t;
        if(x<=100000&&x>0&&d[x]==-1)   
        {
            d[x]=d[t]+1;
            q.push(x);
        }
    }
}

int main()
{
    cin>>n>>k;
    if(n>k)//玄学优化
    {
        printf("%d",n-k);
        return 0;
    }
    else cout<<bfs()<<endl;
}

三、多源bfs

广度优先遍历的正确性:满足两段性和单调性

即1.在访问完所有的第i层节点后,才会开始搜索第i+1层节点。2.任意时刻,队列中至多有两个层次的结点。若其中一部分节点属于第i层,则另一部分节点属于第i+1层,并且所有第i层节点排在第i+1层节点之前。也就是说广度优先队列中的元素满足单调性两段性

本题思路: 求出每个0点到距离自己最近的1点的曼哈顿距离。

可以反向思考用每个1点作为虚拟原点,更新周围的0点的距离。

每个1点的距离显然是0。拓展每个节点时,把新元素加入队尾,可以使得队列中永远只有dist为x,与x+1的元素,并且dist为x的元素一定在dist为x+1的元素前面。即满足单调性和两段性。因此做法是正确的。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define x first
#define y second
using namespace std;

typedef pair<int,int> PII;
const int N =1010;

char g[N][N];
int n,m;
int dist[N][N];

void bfs()
{
    queue<PII> q;
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
    memset(dist,-1,sizeof dist);

    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            if(g[i][j]=='1')
            {
                q.push({i,j});
                dist[i][j]=0;
            }
        }


    while(q.size())
    {
        auto t=q.front();
        q.pop();

        for(int i=0;i<4;i++)
        {
            int a=t.x+dx[i],b=t.y+dy[i];
            if(a<0||b<0||a>=n||b>=m) continue;
            if(dist[a][b]!=-1) continue;

            dist[a][b]=dist[t.x][t.y]+1;
            q.push({a,b});//注意这个放在队尾的性质   
        }
    }


}


int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++) cin>>g[i];

    bfs();
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++) cout<<dist[i][j]<<" ";
        cout<<endl;
    }
    return 0;


}


作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4206207/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

四、最小步数模型

 分析:求路径—存前驱 状态抽象为字符串 为方便从标准态反向搜索 要求字典序最小—搜索方案按abc的顺序搜。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<unordered_map>
using namespace std;

char g[2][4];
unordered_map<string,int> dist;
unordered_map<string,pair<char,string>> pre;


void set(string state)//把字符串转化成数组魔板,,方便状态转移
{
    for(int i=0;i<4;i++) g[0][i]=state[i];
    for(int i=7,j=0;j<4;i--,j++) g[1][j]=state[i];
}

string get()
{
    string res;
    for (int i = 0; i < 4; i ++ ) res += g[0][i];
    for (int i = 3; i >= 0; i -- ) res += g[1][i];    
    return res;
}

string move0(string state)
{
    set(state);

    for(int i=0;i<4;i++) swap(g[0][i],g[1][i]);

    return get();

}

string move1(string state)
{
    set(state);

    int v0=g[0][3],v1=g[1][3];

    for(int i=3;i>0;i--)
        g[0][i]=g[0][i-1],g[1][i]=g[1][i-1];
    g[0][0]=v0,g[1][0]=v1;

    return get();
}

string move2(string state)
{
    set(state);
    int v = g[0][1];
    g[0][1] = g[1][1];
    g[1][1] = g[1][2];
    g[1][2] = g[0][2];
    g[0][2] = v;    


    return get();
}


int bfs(string start,string end)
{
    if(start==end) return 0;
    queue<string> q;

    q.push(start);
    dist[start]=0;

    while(q.size())
    {
        auto t=q.front();
        q.pop();

        string m[3];
        m[0]=move0(t);
        m[1] = move1(t);
        m[2] = move2(t);
        for(int i=0;i<3;i++)//按abc的顺序搜 就是字典序最小的方案
        {
            if(!dist.count(m[i]))
            {
                dist[m[i]]=dist[t]+1;
                pre[m[i]]={'A'+i,t};
                q.push(m[i]);
                if(m[i]==end) return dist[m[i]];
            }
        }
    }
    return -1;
}

int main()
{
    int x;
    string start,end;
    for(int i=0;i<8;i++)
    {
        cin>>x;
        end+=char(x+'0');
    }

    for(int i=1;i<=8;i++) start+=char(i+'0');

    int step=bfs(start,end);

    cout<<step<<endl;
    string res;
    while(end != start)
    {
        res+=pre[end].first;
        end=pre[end].second;
    }

    reverse(res.begin(),res.end());
    if(step>0) cout<<res<<endl;
    return 0;
}

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4208297/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值