搜索之BFS刷题

1.记录位置信息:https://www.acwing.com/problem/content/1078/

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
queue<pair<int,int> > q;
int n;
int a[1010][1010];
pair<int,int>pre[1010][1010];
bool vis[1010][1010];
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};
void bfs(){
    vis[n][n]=1;
    pre[n][n]={-1,-1};
    q.push({n,n});
    while(!q.empty()){
        pair<int,int> ck=q.front();
        q.pop();
        if(ck.x==1&&ck.y==1) break;
        for(int i=0;i<4;i++){
            int sx=ck.x+dx[i],sy=ck.y+dy[i];
            if(a[sx][sy]) continue;
            if(sx<1||sx>n||sy<1||sy>n) continue;
            if(vis[sx][sy]) continue;
            vis[sx][sy]=1;
            pre[sx][sy].x=ck.x;
            pre[sx][sy].y=ck.y;
            q.push({sx,sy});
        }
    }
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>a[i][j];
        }
    }
    //从后往前搜
    bfs();
    int i=1,j=1;
    cout<<i-1<<" "<<j-1<<endl;
    //cout<<pre[n-1][n].x<<" "<<pre[n-1][n].y<<"gkggkkg";
    //cout<<pre[3][5].x<<"gfdf"<<pre[3][5].y<<"dfsdf";
    while(i!=n||j!=n){
        cout<<pre[i][j].x-1<<" "<<pre[i][j].y-1<<endl;
        int i1=pre[i][j].x,j1=pre[i][j].y;
        i=i1;
        j=j1;
    }
    //cout<<n-1<<" "<<n-1;
}

2.最小步数模型:https://www.acwing.com/problem/content/1109/

这里注意两种形态上的转化:

AC代码:

#include<bits/stdc++.h>
using namespace std;
map<string,int> dist;
map<string,pair<char,string> > pre;
queue<string> q;
char g[2][4];
void set1(string ss){
    for(int i=0;i<4;i++) g[0][i]=ss[i];
    for(int i=3,j=4;i>=0;j++,i--) g[1][i]=ss[j];
}
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 mo1(string ss){
    set1(ss);
    for(int i=0;i<4;i++) swap(g[0][i],g[1][i]);
    return get();
}
string mo2(string state)
{
    set1(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 mo3(string state)
{
    set1(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;
    q.push(start);
    dist[start]=0;
    while(!q.empty()){
        auto t=q.front();
        q.pop();
        string m[3];
        m[0]=mo1(t);
        m[1]=mo2(t);
        m[2]=mo3(t);
        for(int i=0;i<3;i++){
            string k=m[i];
            if(dist.count(k)==0){
                dist[k]=dist[t]+1;
                pre[k]={char(i+'A'),t};
                if(k==end) return dist[end];
                q.push(k);
            }
        }
    }
    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=0;i<8;i++) start+=char(i+'1');
    int ck=bfs(start,end);
    cout<<ck<<endl;
    string res;
    while(end!=start){
       res+=pre[end].first;
        end=pre[end].second;
    }
    reverse(res.begin(), res.end());
    if (ck > 0) cout << res << endl;
}

3.双端队列广搜:https://www.acwing.com/problem/content/177/

这一题主要考的就是双端队列,但是我觉得这一题还考察了具体在点和块线上的位置移动实现过程。

我们要知道点与格子是两码事:

假如一个点位于(1,1),那么它可以往左上脚走(-1,-1),而他经过的格子是(0,0)。

因此我们在写点走的方向时也应对应好走过的格子的方向。

具体见代码:

#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = a; i < b; i++)
#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 510;
int dir[4][2] = {{-1, -1}, {-1, 1}, {1, 1}, {1, -1}}; //4个方向移动后点的坐标
int diri[4][2] = {{-1, -1}, {-1, 0}, {0, 0}, {0, -1}}; //移动后方格的坐标(左上角代表方格)
char cs[5] = {'\\', '/', '\\', '/'};
char g[N][N];
int dis[N][N];
bool st[N][N];
int n, m, T;

int bfs()
{
    memset(dis, 0x3f, sizeof dis);
    memset(st, 0, sizeof st);
    dis[0][0] = 0;
    deque<PII> q;
    q.push_back({0, 0});
    while(q.size())
    {
        auto t = q.front();
        q.pop_front();

        if(t.x == n && t.y == m) //双端队列bfs第一次遇到一定是 最短距离
            break;
        if(st[t.x][t.y])
            continue;
        st[t.x][t.y] = 1;

        rep(i, 0, 4)
        {
            int a = t.x + dir[i][0], b = t.y + dir[i][1];//下一个点的坐标
            if(a < 0 || a > n || b < 0 || b > m)    continue; 
            int ca = t.x + diri[i][0], cb = t.y + diri[i][1]; //下个方格对应的坐标
            int d = dis[t.x][t.y] + (cs[i] != g[ca][cb]); //当前的节点到相邻节点的距离
            //如果可以更新最短距离
            if(d < dis[a][b]) //注意,我们需要用点的坐标,方格坐标只是用来判断 “边权” 的
            {
                dis[a][b] = d;
                //注意我们的研究对象,是 点 的坐标的奥
                if(cs[i] == g[ca][cb]) //距离为0,那么下一次继续遍历这个点,实现了贪心的“最短”
                    q.push_front({a, b});
                else 
                    q.push_back({a, b});
            }

        }
    }

    return dis[n][m]; //返回到右下角最短距离
}
int main()
{
    cin >> T;
    while(T -- )
    {
        cin >> n >> m; //x、y之和为奇数的点必然不能到达
        rep(i, 0, n)    scanf("%s", &g[i]);
        int t = bfs();
        if(t == 0x3f3f3f3f)
            puts("NO SOLUTION");
        else 
            printf("%d\n", t);
    }
}

4.双向BFS:https://www.acwing.com/problem/content/192/

注意一下几个点:

1.假如一个队列空了,那么从头到结尾与从结尾到头不连通。

2.每一次BFS扩展的时候总是挑元素少的队列,这样效率更高。

AC代码:

#include<bits/stdc++.h>
using namespace std;
string A,B;
string a[100],b[100];
int k=1;
map<string,int> dis1;
map<string,int> dis2;
queue<string> qa;
queue<string> qb;
int ext(queue<string>& qa,map<string,int>& dis1,map<string,int>& dis2,string a[],string b[]){
    int ck=dis1[qa.front()];
    
    while(!qa.empty()&&dis1[qa.front()]==ck){
        auto t=qa.front();
        qa.pop();
        for(int i=1;i<=k;i++){
            for(int j=0;j<t.size();j++){
                if(a[i]!=t.substr(j,a[i].size())) continue;
                string hh=t.substr(0,j)+b[i]+t.substr(j+a[i].size());
                if(dis1.count(hh)!=0) continue;
                dis1[hh]=dis1[t]+1;
                if(dis2.count(hh)!=0){
                    return dis1[hh]+dis2[hh];
                }
                qa.push(hh);

            }
        }
    }
    return 11;
}
int bfs(){
    if (A == B) return 0;
    qa.push(A);
    qb.push(B);
    dis1[A]=0,dis2[B]=0;
    int step=0;
    while(!qa.empty()&&!qb.empty()){
        int t;
        if(qa.size()<=qb.size()) t=ext(qa,dis1,dis2,a,b);
        else t=ext(qb,dis2,dis1,b,a);
        if(t<=10) return step+1;
        step++;
        if(step==10) return 11;
    }
    return 11;
}
int main(){
    cin>>A>>B;
    while(cin>>a[k]>>b[k]) k++;
    int t=bfs();
    if(t>10) cout<<"NO ANSWER!"<<endl;
    else cout<<t;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值