电路维修(搜索、双端队列BFS)————《算法竞赛进阶指南》

电路维修

达达是来自异世界的魔女,她在漫无目的地四处漂流的时候,遇到了善良的少女翰翰,从而被收留在地球上。

翰翰的家里有一辆飞行车。

有一天飞行车的电路板突然出现了故障,导致无法启动。

电路板的整体结构是一个 R 行 C 列的网格(R,C≤500),如下图所示。

电路.png

每个格点都是电线的接点,每个格子都包含一个电子元件。

电子元件的主要部分是一个可旋转的、连接一条对角线上的两个接点的短电缆。

在旋转之后,它就可以连接另一条对角线的两个接点。

电路板左上角的接点接入直流电源,右下角的接点接入飞行车的发动装置。

达达发现因为某些元件的方向不小心发生了改变,电路板可能处于断路的状态。

她准备通过计算,旋转最少数量的元件,使电源与发动装置通过若干条短缆相连。

不过,电路的规模实在是太大了,达达并不擅长编程,希望你能够帮她解决这个问题。

注意:只能走斜向的线段,水平和竖直线段不能走。

输入格式
输入文件包含多组测试数据。

第一行包含一个整数 T,表示测试数据的数目。

对于每组测试数据,第一行包含正整数 R 和 C,表示电路板的行数和列数。

之后 R 行,每行 C 个字符,字符是"/“和”"中的一个,表示标准件的方向。

输出格式
对于每组测试数据,在单独的一行输出一个正整数,表示所需的最小旋转次数。

如果无论怎样都不能使得电源和发动机之间连通,输出 NO SOLUTION。

数据范围
1≤R,C≤500,
1≤T≤5
输入样例:
1
3 5
\/\
\///
/\\
输出样例:
1

题解

前置知识:迪杰特斯拉算法:每次拿出一个距离起点最短的点并且这个点之前没有被拿出过,用这个点来更新到所有点的距离,直到所有点都拿出过一次,停止,此时就求得了每个点的单源最短路

此题使用双端队列实现迪杰特斯拉算法可求得最短的路线
分析:
双端队列主要解决图中边的权值只有0或者1的最短路问题
假设:题中如果需要旋转电缆,那么设这个边的边权为1,如果不需要旋转,则这个边边权为0。
题目就转化为 ——> 图中边的权值只有0或者1的最短路问题

每次的操作:每次从队头取出一个元素,拓展此点到其他点:

  • 如果边权为 0 ,加入队头
  • 如果边权为1,加入队尾
    在这里插入图片描述
    图片来源于 https://www.acwing.com/solution/content/21775/

下面代码中

  • n e x t d [ 4 ] [ 2 ] nextd[4][2] nextd[4][2]数组表示从一个点去往其他点的关系 (图中的dx[]和dy[])
  • n e x t i [ 4 ] [ 2 ] nexti[4][2] nexti[4][2]数组表示从一个点去往其他点需要踩过的格子的位置 之间的关系 (图中的ix[]和iy[])
  • c s [ ] cs[] cs[]表示当前点走到4个方向的点理想状态下格子形状(边权是0的状态)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>
using namespace std;
typedef pair<int, int> PII;
#define x first
#define y second

const int N = 505,M=N*N;
int n,m;
char g[N][N];
int dist[N][N];
bool st[N][N];
int bfs()
{
    memset(st, 0, sizeof st);
    memset(dist,0x3f,sizeof dist);
    dist[0][0]=0;
    deque<PII> q;
    q.push_back({0,0});
    char cs[] = "\\/\\/";
    int nextd[4][2]={{-1,-1},{-1,1},{1,1},{1,-1}};
    int nexti[4][2]={{-1,-1},{-1,0},{0,0},{0,-1}};
    while(q.size()){
        PII t = q.front();
        q.pop_front();
        if(st[t.x][t.y]) continue;
        st[t.x][t.y]=true;
        
        for(int i=0; i<4; ++i){
            int a = t.x + nextd[i][0], b = t.y + nextd[i][1];
            if(a<0 || a>n || b<0 || b>m) continue;
            int ca = t.x + nexti[i][0], cb = t.y + nexti[i][1];
            int d = dist[t.x][t.y] + (g[ca][cb]!=cs[i]);
            
            if(d < dist[a][b]){
                dist[a][b] = d;
                
                if(g[ca][cb]!=cs[i]) q.push_back({a,b});
                else q.push_front({a,b});
            }
        }
    }
    return dist[n][m];
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d", &n, &m);
        for(int i=0; i<n; ++i) scanf("%s",g[i]);
        int t = bfs();
        if(t == 0x3f3f3f3f) puts("NO SOLUTION");
        else printf("%d\n",t);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
DFS算法是一种用于图遍历或树遍历的算法。其核心思想是从起点开始递归地深入每一个可能的分支,直到无法继续为止,然后回溯到上一个节点,继续尝试其他分支。 DFS算法有两种实现方式:递归实现和非递归实现(使用栈)。 递归实现的DFS算法伪代码: ``` DFS(node): if node is None: return visit(node) for child in node.children: DFS(child) ``` 非递归实现的DFS算法伪代码: ``` DFS(node): stack = [node] while stack: node = stack.pop() if node.visited: continue visit(node) node.visited = True for child in node.children: stack.append(child) ``` 其中,visit(node)表示对节点node进行操作,例如打印节点值、记录路径等。 DFS算法的时间复杂度为O(V+E),其中V为节点数,E为边数。因为每个节点和每条边都只会被访问一次。 下面是一个例题,用DFS算法求解从起点到终点的路径(leetcode 79题): 给定一个二维网格和一个单词,找出该单词是否存在于网格中。 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。 示例: ``` board = [ ['A','B','C','E'], ['S','F','C','S'], ['A','D','E','E'] ] 给定 word = "ABCCED", 返回 true. 给定 word = "SEE", 返回 true. 给定 word = "ABCB", 返回 false. ``` 实现代码: ```python class Solution: def exist(self, board: List[List[str]], word: str) -> bool: if not board or not board[0]: return False m, n = len(board), len(board[0]) visited = [[False] * n for _ in range(m)] directions = [(0, 1), (0, -1), (1, 0), (-1, 0)] def dfs(i, j, k): if k == len(word): return True if i < 0 or i >= m or j < 0 or j >= n or visited[i][j] or board[i][j] != word[k]: return False visited[i][j] = True for dx, dy in directions: if dfs(i+dx, j+dy, k+1): return True visited[i][j] = False return False for i in range(m): for j in range(n): if dfs(i, j, 0): return True return False ``` 时间复杂度为O(m*n*3^k),其中m、n为网格大小,k为单词长度。因为每个格子都有可能走,所以是O(m*n),而每次调用dfs函数会向四个方向递归,每个方向都不能重复走,所以是3^k。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

葛济维的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值