AcWing 175. 电路维修(双端队列BFS)

题目链接:点击这里
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我们可以把电路板上的每个格点(横线与竖线的交叉点)看作无向图中的节点。若两个节点 x x y y 是某个小方格的两个对角,则在 x x y y 之间连边。若该方格中的标准件(对角线)与 x x y y 的线段重合,则边权为 0 0 ;若垂直相交,则边权为 1 1 (说明需要旋转 1 1 次才能连上)。然后,我们在这个无向图中求出左上角到右下角的最短距离,就得到了答案。

这是一张边权要么是 0、要么是 1 的无向图。在这样的图上,我们可以通过双端队列广搜来计算。算法的整体框架与一般的广搜类似,只是在每个节点上沿分支扩展时稍作改变。如果这条分支是边权为0 的边,就把沿该分支到达的新节点从队头入队;如果这条分支是边权为 1 的边,就像一般的广搜一样从队尾入队。这样一来,我们就仍然能保证,任意时刻广搜队列中的节点对应的距离值都具有“两段性”和“单调性”,每个节点第一次被访问时,就能得到从左上角到该节点的最短距离。

因为每个节点只需要访问一次,所以算法的时间复杂度为 O ( R C ) O(R*C)

本题还有一个性质,其实网格格点有一半是永远走不到的(如下图红色的点),所以可以特判一下。

细节处理:每个格子的编号和每个格点的编号不一样,注意两对增量数组的写法。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<deque>
#include<algorithm>

using namespace std;
typedef pair<int,int> PII;
const int N = 510;

int dx[] = {-1, -1, 1, 1};      // 当前格点四周的新格点
int dy[] = {-1, 1, 1, -1};

int ix[] = {-1, -1, 0, 0};      // 当前格点四周的格子
int iy[] = {-1, 0, 0, -1};

char cs[5] = "\\/\\/";

int n, m;
char g[N][N];
int d[N][N];
bool st[N][N];                  // 存储每个点的最短路是否已经确定

int bfs()
{
    deque<PII> q;
    memset(d, 0x3f, sizeof d);
    memset(st, false, sizeof st);
    
    q.push_back({0, 0});
    d[0][0] = 0;
    
    while(q.size())
    {
        auto t = q.front();
        q.pop_front();
        
        int x = t.first, y = t.second;       // (x,y)简化后续代码
        
        if(x == n && y == m)    return d[x][y];
        
        if(st[x][y])    continue;
        
        st[x][y] = true;
        
        for(int i = 0; i < 4; ++i)
        {
            int a = x + dx[i], b = y + dy[i];       // (x,y)扩展出的新格点(a,b)
            
            if(a < 0 || a > n || b < 0 || b > m)    continue;
            
            int ga = x + ix[i], gb = y + iy[i];     // (x,y)扩展出的格子(ga,gb)
            
            int dist = d[x][y] + (g[ga][gb] != cs[i]);

            if(dist < d[a][b])
            {
                d[a][b] = dist;

                if(g[ga][gb] != cs[i])  q.push_back({a, b});
                else    q.push_front({a, b});
            }
        }
    }
    
    return -1;      // 不会被执行到
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &n, &m);      // n行m列的格子
        for(int i = 0; i < n; ++i)  scanf("%s", g[i]);
        
        if((n + m) & 1)   puts("NO SOLUTION");
        else    printf("%d\n", bfs());
    }
    
    return 0;
}
发布了844 篇原创文章 · 获赞 135 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/qq_42815188/article/details/105082537