《算法竞赛进阶指南》0x26广搜变形 电路维修 01最短路问题

题目链接:https://www.acwing.com/solution/content/8249/

将两个结点之间能直接相连的边长视为0,不能直接相连的边长视为1,因为需要逆转,所以这个问题就变成了N*M个结点的最短路问题,可以通过双端队列搜索进行优化,

因为在队列中的点具有“两段性”和“单调性”,所以对于取出的点,如果以同样的值更新了一个点(i,j)那这个点的权值一定是双端队列中最小的(可以跟队首的某些一样,但觉度不会大于队首)。

由于队列中只会有两个层次的结果,也就是只会有两种值(“两段性”),所以队首+1后放到队尾也一定是满足两段性和单调性的。

问题中有一个需要注意的地方,就是一个点一旦被更新了之后可能还被更新,比如队首有两个点(i1,j1)和(i2,j2)他们的distance相同,但是队首的元素+1更新了一个点(x,y),第二个元素可能以

distance+0更新点(x,y),所以有必要对每个点都不断迭代。

注意:每个点第一次出队的时候一定就是最短的了,因为如果要更新他的最短路一定是通过队列中的其他点的,但是其他点的长度不比他短。

代码:

#include<iostream>
#include<cstring>
#include<deque>
using namespace std;
#define maxn 510
char s[maxn][maxn];
int dx[]={-1,-1,1,1},dy[]={-1,1,1,-1};//点的坐标偏移 
int ix[]={-1,-1,0,0},iy[]={-1,0,0,-1};    
char ss[]="\\/\\/";
int n,m;
bool vis[maxn][maxn];
int d[maxn][maxn];
int bfs(){
    deque< pair<int,int> > dq;

    dq.push_back({0,0});//将起点存入队列

    memset(d,0x3f,sizeof(d));
    d[0][0]=0;
    
    while(!dq.empty()){
        pair<int,int> p=dq.front();
        dq.pop_front();
        int x=p.first,y=p.second;
        if(x==n && y==m)return d[x][y];
        for(int i=0;i<4;i++){
            int xx=x+dx[i];
            int yy=y+dy[i];
            if(xx<0 || xx>n || yy<0 || yy>m)continue;
            int px=x+ix[i];
            int py=y+iy[i];
            int w=0;
            if(s[px][py]!=ss[i])w=1;//输入与预期是垂直的,边长是1 
            if(d[xx][yy] > d[x][y]+w){
                d[xx][yy]=d[x][y]+w;
                if(w)dq.push_back({xx,yy});//边长度为1的插入队尾,长度为0的插入队首 
                else dq.push_front({xx,yy});
            }
        }
    } 
    return d[n+1][m+1]; 
}
int main(){
    int T;
    cin>>T;
    while(T--){
        cin>>n>>m;
        for(int i=0;i<n;i++)scanf("%s",s[i]);
        int ans=bfs();
        if(ans==0x3f3f3f3f)cout<<"NO SOLUTION"<<endl;
        else cout<<ans<<endl;
    }
}

 加上一个vis之后的代码,使用双端队列可以对dijkstra算法至少优化掉一个log,使用双端队列也是对dijkstra中的heap的优化,主要是利用了题目的特性。

#include<iostream>
#include<cstring>
#include<deque>
using namespace std;
#define maxn 510
char s[maxn][maxn];
int dx[]={-1,-1,1,1},dy[]={-1,1,1,-1};//点的坐标偏移 
int ix[]={-1,-1,0,0},iy[]={-1,0,0,-1};    
char ss[]="\\/\\/";
int n,m;
bool vis[maxn][maxn];
int d[maxn][maxn];
int bfs(){
    deque< pair<int,int> > dq;

    dq.push_back({0,0});//将起点存入队列
    
    memset(d,0x3f,sizeof(d));
    d[0][0]=0;
    memset(vis,0,sizeof(vis));
    while(!dq.empty()){
        pair<int,int> p=dq.front();
        dq.pop_front();
        
        int x=p.first,y=p.second;
        if(vis[x][y])continue;
        vis[x][y]=1;
        if(x==n && y==m)return d[x][y];
        for(int i=0;i<4;i++){
            int xx=x+dx[i];
            int yy=y+dy[i];
            if(xx<0 || xx>n || yy<0 || yy>m)continue;
            int px=x+ix[i];
            int py=y+iy[i];
            int w=0;
            if(s[px][py]!=ss[i])w=1;//输入与预期是垂直的,边长是1 
            if(d[xx][yy] > d[x][y]+w){
                d[xx][yy]=d[x][y]+w;
                if(w)dq.push_back({xx,yy});//边长度为1的插入队尾,长度为0的插入队首 
                else dq.push_front({xx,yy});
            }
        }
    } 
    return d[n+1][m+1]; 
}
int main(){
    int T;
    cin>>T;
    while(T--){
        cin>>n>>m;
        for(int i=0;i<n;i++)scanf("%s",s[i]);
        int ans=bfs();
        if(ans==0x3f3f3f3f)cout<<"NO SOLUTION"<<endl;
        else cout<<ans<<endl;
    }
}

猜你喜欢

转载自www.cnblogs.com/randy-lo/p/13169293.html