搜索——最短路模型&&多源BFS&&最小步数模型(BFS)

最短路模型

迷宫问题

原题地址

这里是引用

#include<iostream>
#include<algorithm>
#include<string>
#include<string.h>
#include<vector>
#include<queue>
#include<set>
#include<map>
#define int long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int> pii;
const int N= 1010;
int g[N][N];
int n;
bool st[N][N];
pii pr[N][N];
int dx[]={-1,0,1,0},dy[]={0,1,0,-1};

void bfs(int x,int y){
    queue<pii> q;
    q.push({x,y});
    st[x][y]=true;
    while(q.size()){
        auto t=q.front();
        q.pop();
        for(int i=0;i<4;i++){
            int tx=t.fi+dx[i],ty=t.se+dy[i];
            if(tx<0||tx>=n||ty<0||ty>=n) continue;
            if(g[tx][ty]==1) continue;
            if(st[tx][ty]) continue;
            // if(tx==0&&ty==0){
            //     pr[tx][ty]=t;
            //     break;
            // }这个会过不去,break只是跳出循环,而不是直接退出
            q.push({tx,ty});
            st[tx][ty]=true;
            pr[tx][ty]=t;
        }
    }
}


signed main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    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};//end(0,0)
    while(!(end.fi==n-1&&end.se==n-1)){
        cout<<end.fi<<" "<<end.se<<endl;
        // end.fi=pr[end.fi][end.se].fi;
        // end.se=pr[end.fi][end.se].se;
        end=pr[end.fi][end.se];//上面代码会tle
    }
    cout<<n-1<<" "<<n-1;
    return 0;
}

debug

1.TLE部分——赋值end.fi的同时把end改变了
2.if 语句会卡,过不去。bfs本质是第一次找到的最小路径(距离),如果用break只是跳出for循环,如果后面还有更长路径能到达终点,那么最短路径就会被更新为后面的那条长些的路径。所以第一次找到后就应该跳出while循环,直接退出函数,用return即可。

总结

  1. PII pair的用法
  2. 求路径的技巧。起点开始,后一个点通过当前 pr数组 信息来获得,最后到达终点停止(看成一个个点移动)

武士风度的牛

原题地址

这里是引用

#include<iostream>
#include<algorithm>
#include<string>
#include<string.h>
#include<vector>
#include<map>
#include<set>
#include<queue>
#define int long long
#define inf 0x3f3f3f3f
#define fi first
#define se second
using namespace std;
typedef pair<int,int> pii;
const int N=200;

char g[N][N];
int dist[N][N];
int n,m;
bool st[N][N];
pii q[N*N];
int dx[8]={-2,-2,-1,1,2,2,1,-1},dy[8]={-1,1,2,2,1,-1,-2,-2};

int bfs(int x,int y){
    
    memset(dist,-1,sizeof dist);
    // queue<pii> q;
    // q.push({x,y});
    int hh=0,tt=0;
    q[0]={x,y};
    dist[x][y]=0;
    st[x][y]=true;
    while(hh<=tt){
        auto t=q[hh++];
        for(int i=0;i<8;i++){
            int fx=t.fi+dx[i],fy=t.se+dy[i];
            if(fx<0||fx>=n||fy<0||fy>=m) continue;
            if(g[fx][fy]=='*') continue;
            if(dist[fx][fy]!=-1) continue;
            if(st[fx][fy]) continue;
            if (g[fx][fy] == 'H') return dist[t.fi][t.se] + 1;//不return会被覆盖
            st[fx][fy]=true;
            q[++tt]={fx,fy};
            dist[fx][fy]=dist[t.fi][t.se]+1;
        }
    }
}

signed main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>m>>n;
    
    int sx,sy,tx,ty;
    // for (int i = 0; i < n; i ++ ) cin >> g[i];
    
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++)
        {
            cin>>g[i][j];
            if(g[i][j]=='K'){
                sx=i,sy=j;
            }
            if(g[i][j]=='H')
                tx=i,ty=j;
        }
    } 
    cout<<bfs(sx,sy);
    // cout<<dist[tx][ty];答案错误会被覆盖,是先直接return的,还没有更新dist,所以还是-1
    // for(int i=0;i<n;i++){
    //     for(int j=0;j<m;j++)
    //         cout<<dist[i][j]<<" ";
    //     cout<<endl;
    // }
    return 0;
}

debug

  1. 没有return,让while循环一直执行到最后,最后的距离不是最短的,会被覆盖掉。
  2. 返回dist[tx][ty]没有注意细节,因为是先return在更新的,所以终点的dist没有被更新,所以如果输出dist终点的话,会是-1;
  3. 如果不return,会发生Segmentation Fault 错误,内存超了
  4. 用st和dist都可以做判断标记
  5. tx,ty读入的时候逗号写成分号,导致ty不是正确坐标。最后debug半天必须用函数里面的回来,麻了,下次注意,要么就老实写大括号包起来。

总结

  1. bfs是求最短的性质,所以第一个碰到的点肯定是最短了,就直接return就行,切记不要用break,不然可以只是退出for循环。
  2. 用dist当标记数组
  3. 马走法的方法。dx[8]={-2,-2,-1,1,2,2,1,-1},dy[8]={-1,1,2,2,1,-1,-2,-2}

抓住那头牛

原题链接

这里是引用

#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<vector>
#include<queue>
#include<set>
#include<map>
#define int long long
#define fi first
#define se second
#define inf 0x3f3f3f3f3f
using namespace std;
typedef pair<int, int>pii;

const int N = 1e5+10;
int n,k;
int dist[N];

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

signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin>>n>>k;
    cout<<bfs(n);
    return 0;
}

总结

这题写得很顺利,对于搜索,关键在于找到搜索的方向,考虑怎么去扩展节点。

模型归纳

  1. bfs有最短的性质,一般第一次搜索到的点就是就有最短的性质,(弹出的队头也是)。如果是双向队列的话,就用第一次弹出的队头,因为第一次放进去的队尾不一定是最短的。
  2. 注意细节,理解标记的作用,体会每次dist是怎么更新的。

多源BFS

矩阵距离

原题链接

这里是引用

#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<vector>
#include<queue>
#include<set>
#include<map>
#define int long long
#define fi first
#define se second
#define inf 0x3f3f3f3f3f
using namespace std;
typedef pair<int, int>pii;

const int N = 1100;
int n,m;
char g[N][N];
bool st[N][N];
int dist[N][N];
int dx[]={-1,0,1,0},dy[]={0,1,0,-1};
void bfs(){
    memset(dist ,-1,sizeof dist );
    queue<pii> q;
    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 tx=t.fi+dx[i],ty=t.se+dy[i];
            if(tx<0||tx>=n||ty<0||ty>=m) continue;
            if(dist[tx][ty]!=-1) continue;
            q.push({tx,ty});
            dist[tx][ty]=dist[t.fi][t.se]+1;
        }
    }
    
}
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    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;
}

总结

  1. 哈曼顿距离的定义了解一下
  2. 实际是多源点最短路问题,还是利用队列的两段性,将所有源点放进去搜索即可

最小步数模型

模板

原题链接

这里是引用

#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<vector>
#include<queue>
#include<set>
#include<map>
#define int long long
#define fi first
#define se second
#define inf 0x3f3f3f3f3f
using namespace std;
typedef pair<string,char>pii;

const int N = 1100;
string s,ed="12345678";
queue<string> q;
map<string,int> dist;
map<string,pii> pre;

string change(string t,int k){
    string p;
    if(k==0) p={t[7],t[6],t[5],t[4],t[3],t[2],t[1],t[0]};
    if(k==1) p={t[3],t[0],t[1],t[2],t[5],t[6],t[7],t[4]};
    if(k==2) p={t[0],t[6],t[1],t[3],t[4],t[2],t[5],t[7]};
    return p;
}

int bfs(string p){
    q.push(p);
    dist[p]=0;
    while(q.size()){
        auto t=q.front();
        q.pop();
        if(t==s) return dist[t];
        for(int i=0;i<3;i++){
            auto state=change(t,i);
            if(dist.count(state)) continue;
            q.push(state);
            dist[state]=dist[t]+1;
            pre[state]={t,'A'+i};
        }
    }
}

signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    for(int i=0;i<8;i++){
        int x;
        cin>>x;
        s+=(x+'0');
    }
    if(s==ed){
        cout<<0;
        return 0;
    }
    cout<<bfs(ed)<<endl;//不能bfs(s),因为题意是要从基本状态开始
    
    string t="",p=s;
    while(true){
        // cout<<p<<"   "<<pre[p].fi<<"  "<<pre[p].se<<endl;
        t+=pre[p].se;
        if(pre[p].fi==ed) break;
        p=pre[p].fi;
    }
    reverse(t.begin(),t.end());
    cout<<t;
    
    
    // string t="",p=ed;
    // while(true){
    //     // cout<<pre[p].fi<<"  "<<pre[p].se<<endl;
    //     t+=pre[p].se;
    //     if(pre[p].fi==s) break;
    //     p=pre[p].fi;
    // }
    // // cout<<t<<endl;
    
    return 0;
}

debug

题目没有看清楚,问的是从标准状态开始的字典序最小,所以要用ed开始搜索。最后注意反转输出就行。

总结

这题还是最短路性质,只不过每次扩展方向是改变的全局,然后就是注意输出路经的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值