• 《算法竞赛进阶指南》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;
        }
    }
  • 相关阅读:
    接口运用实例
    C# Lambda表达式运用
    图片转换图片流方法(二进制流)
    简单的winform编辑器
    C# OO(初级思想)。
    MVC知识点
    提高sql查询效率
    DataRead 和DataSet区别
    JavaScript内置对象与原生对象【转】
    Cookie,Sesstion,Application 缓存。
  • 原文地址:https://www.cnblogs.com/randy-lo/p/13169293.html
Copyright © 2020-2023  润新知