电路维修(双端队列 & 最短路)
达达是来自异世界的魔女,她在漫无目的地四处漂流的时候,遇到了善良的少女翰翰,从而被收留在地球上。
翰翰的家里有一辆飞行车。
有一天飞行车的电路板突然出现了故障,导致无法启动。
电路板的整体结构是一个$R$行$C$列的网格(R,C≤500),如下图所示。

每个格点都是电线的接点,每个格子都包含一个电子元件。
电子元件的主要部分是一个可旋转的、连接一条对角线上的两个接点的短电缆。
在旋转之后,它就可以连接另一条对角线的两个接点。
电路板左上角的接点接入直流电源,右下角的接点接入飞行车的发动装置。
达达发现因为某些元件的方向不小心发生了改变,电路板可能处于断路的状态。
她准备通过计算,旋转最少数量的元件,使电源与发动装置通过若干条短缆相连。
不过,电路的规模实在是太大了,达达并不擅长编程,希望你能够帮她解决这个问题。
输入格式
输入文件包含多组测试数据。
第一行包含一个整数T,表示测试数据的数目。
对于每组测试数据,第一行包含正整数R和C,表示电路板的行数和列数。
之后R行,每行C个字符,字符是"/"和"\"中的一个,表示标准件的方向。
输出格式
对于每组测试数据,在单独的一行输出一个正整数,表示所需的缩小旋转次数。
如果无论怎样都不能使得电源和发动机之间连通,输出NO SOLUTION。
emmmmm, 一道《算法进阶》上的题, 书中所给的方法是双端队列, 但身为蒟蒻的我怎么可能会,所以我想到了SPFA算法。
把每一个格点当做节点, 则共有(n + 1)* (m + 1)的节点, 在下图中, 就将1到4这条边的边权设为0, 而2到3这条边就为1,

通过这样的建图, 我们以1为起点, 跑一边最短路,输出终点的距离即可,注意的是, 当终点的距离为无限大时, 即到达不了终点, 输出NO SOLUTION;
比较坑的是, 这道题的数据竟然卡SPFA, 所以用Dijkstra就行了
#include <bits/stdc++.h> using namespace std; typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const int MAXM = 3e3 + 10; template < typename T > inline void read(T &x) {
x = 0; T ff = 1, ch = getchar();
while(!isdigit(ch)) {
if(ch == '-') ff = -1;
ch = getchar();
}
while(isdigit(ch)) {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
x *= ff;
} template < typename T > inline void write(T x) {
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
} int T, n, m;
int vis[MAXN], dis[MAXN];
int lin[MAXN], tot = 0;
char ch;
struct edge {
int y, v, next;
}e[MAXN]; inline void add(int xx, int yy, int vv) {
e[++tot].y = yy;
e[tot].v = vv;
e[tot].next = lin[xx];
lin[xx] = tot;
} /*void SPFA() {
memset(dis, 0x3f, sizeof(dis));
memset(vis, false, sizeof(vis));
queue < int > q;
q.push(1);
dis[1] = 0;
while(!q.empty()) {
int x = q.front(); q.pop(); vis[x] = false;
for(int i = lin[x], y; i; i = e[i].next) {
if(dis[y = e[i].y] > dis[x] + e[i].v) {
dis[y] = dis[x] + e[i].v;
if(!vis[y]) {
vis[y] = true;
q.push(y);
}
} }
} }*/ void Dijkstra() {
memset(dis, 0x3f, sizeof(dis));
memset(vis, false, sizeof(vis));
priority_queue < pair < int, int > > q;
dis[1] = 0;
q.push(make_pair(0, 1));
while(!q.empty()) {
int x = q.top().second; q.pop();
if(vis[x]) continue;
vis[x] = true;
for(int i = lin[x], y; i; i = e[i].next) {
if(dis[y = e[i].y] > dis[x] + e[i].v) {
dis[y] = dis[x] + e[i].v;
if(!vis[y]) q.push(make_pair(-dis[y], y));
}
}
}
} int main() {
read(T);
while(T--) {
read(n); read(m);
memset(lin, 0, sizeof(lin));
memset(e, 0, sizeof(e));
tot = 0;
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
ch = getchar();
if(ch == '/') {
add(((i - 1) * (m + 1) + j), i * (m + 1) + j + 1, 1);
add(i * (m + 1) + j + 1, (i - 1) * (m + 1) + j, 1);
add(i * (m + 1) + j, (i - 1) * (m + 1) + j + 1, 0);
add((i - 1) * (m + 1) + j + 1, i * (m + 1) + j, 0);
}
else {
add(((i - 1) * (m + 1) + j), i * (m + 1) + j + 1, 0);
add(i * (m + 1) + j + 1, (i - 1) * (m + 1) + j, 0);
add(i * (m + 1) + j, (i - 1) * (m + 1) + j + 1, 1);
add((i - 1) * (m + 1) + j + 1, i * (m + 1) + j, 1);
}
}
ch = getchar();
}
// SPFA();
Dijkstra();
if(dis[(n + 1) * (m + 1)] == INF) puts("NO SOLUTION");
else write(dis[(n + 1) * (m + 1)]), puts("");
}
return 0;
}
今天, 某位机房大佬教了我双端队列, 思想大概和我上面的相同, 也是建一条0或1的边, 只是在搜索的过程中,将边权为0的加入队头, 1加入队尾, 这样每个节点可能多次入队, 但第一次弹出时就是最短距离
#include <bits/stdc++.h> using namespace std; typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 5e5 + 100;
const int MAXM = 3e3 + 10; template < typename T > inline void read(T &x) {
x = 0; T ff = 1, ch = getchar();
while(!isdigit(ch)) {
if(ch == '-') ff = -1;
ch = getchar();
}
while(isdigit(ch)) {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
x *= ff;
} template < typename T > inline void write(T x) {
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
} int n, m, t;
int dis[MAXM][MAXM];
char ch[MAXM][MAXM]; int BFS() {
deque < pair < int, int > > q;
int dx[5] = {-1, -1, 1, 1}, dy[5] = {-1, 1, 1, -1};
int ix[5] = {-1, -1, 0, 0}, iy[5] = {-1, 0, 0, -1};
memset(dis, 0x3f, sizeof(dis));
q.push_back(make_pair(0, 0));
dis[0][0] = 0;
char s[6] = "\\/\\/";
while(!q.empty()) {
pair < int, int > c = q.front();
q.pop_front();
int x = c.first, y = c.second;
for(int i = 0; i < 4; ++i) {
int u = x + dx[i], v = y + dy[i];
int a = x + ix[i], b = y + iy[i];
if(u > n || u < 0 || v > m || v < 0) continue;
int w = 0;
if(ch[a][b] != s[i]) ++w;
if(dis[u][v] > dis[x][y] + w) {
dis[u][v] = dis[x][y] + w;
if(w) q.push_back(make_pair(u, v));
else q.push_front(make_pair(u, v));
}
}
}
return dis[n][m];
} int main() {
read(t);
while(t--) {
read(n); read(m);
for(int i = 0; i < n; ++i)
scanf("%s", &ch[i]);
int x = BFS();
if(x == INF) puts("NO SOLUTION");
else write(x), puts("");
}
return 0;
}
电路维修(双端队列 & 最短路)的更多相关文章
- CH 2601 - 电路维修 - [双端队列BFS]
题目链接:传送门 描述 Ha'nyu是来自异世界的魔女,她在漫无目的地四处漂流的时候,遇到了善良的少女Rika,从而被收留在地球上.Rika的家里有一辆飞行车.有一天飞行车的电路板突然出现了故障,导致 ...
- Luogu P2243 电路维修 双端队列BFS
当转移的代价是0和一个分明不同的权值时,可以用双端队列BFS去跑(你跑最短路也没问题..QWQ) 而对于这道题,边旋转代价是1,不旋转代价是0:可以直接建图最短路,也可以跑BFS 这个题建图很有意思: ...
- POJ3662 SPFA//二分 + 双端队列最短路
https://cn.vjudge.net/problem/12427/origin 题意:求1到N第K + 1大条边权最小的路径 首先想到dp递推,dp[x][y]表示到x这个点经过y条免费边的最小 ...
- CH2601 电路维修(双端队列bfs)建图恶心
CH2601 电路维修 双端队列bfs,其实就是因为只有0和1所以可以直接2维护队列单调性(和优先队列一个道理) 建图的过程需要仔细斟酌(想一想id为什么这么写) 还有,空间要开够(很玄学),我一开始 ...
- 2601 电路维修 (双端队列bfs\优先队列bfs(最短路))
描述 Ha'nyu是来自异世界的魔女,她在漫无目的地四处漂流的时候,遇到了善良的少女Rika,从而被收留在地球上.Rika的家里有一辆飞行车.有一天飞行车的电路板突然出现了故障,导致无法启动. 电路板 ...
- 电路维修 (广搜变形-双端队列bfs)
# 2632. 「BalticOI 2011 Day1」打开灯泡 Switch the Lamp On [题目描述] 有一种正方形的电路元件,在它的两组相对顶点中,有一组会用导线连接起来,另一组则不会 ...
- POJ 3662 Telephone Lines【二分答案+最短路】||【双端队列BFS】
<题目链接> 题目大意: 在一个节点标号为1~n的无向图中,求出一条1~n的路径,使得路径上的第K+1条边的边权最小. 解题分析:直接考虑情况比较多,所以我们采用二分答案,先二分枚举第K+ ...
- P - The Shortest Path in Nya Graph-hdu4725(双端队列+拆点)
题意:有N个点和N层..一层有X个点(0<=X<=N).两邻两层间有一条路花费C.还有M条小路在两个点之间.问从第一个点走到第N个点最短路是多少... 可以考虑在每一层增加一个点,这个点到 ...
- [lougu2243]双端队列搜索
正统双端队列搜索 回顾:普通队列进行边权为定值的最短路 每次到达都是最优的(意味着不用取min) why? 因为所有状态按照 入队的先后顺序 具有 层次单调性,每次扩展,都往外走一步,满足从起始到该状 ...
随机推荐
- 洛谷P1309——迷宫(傻瓜DFS)
题目背景 迷宫 [问题描述] 给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过.给定起点坐标和 终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案.在迷宫 中移动有上下 ...
- mybatis一对一联表查询的两种常见方式
1.一条语句执行查询(代码如下图) 注释:class表(c别名),teacher表(t别名)teacher_id为class表的字段t_id为teacher表的字段,因为两者有主键关联的原因,c_i ...
- PHP中的文件系统函数(二)
这次我们来学习的是一些不是太常用,但却也非常有用的一些函数.它们中有些大家可能见过或者使用过,有一些可能就真的没什么印象了.它们都是 PHP 中文件系统相关操作函数的一部分.存在即合理,或许只是我们的 ...
- javascript 比较版本号大小 字符串
* 不用系统比较大小的函数 // 不考虑字母 function s2i(s) { return s.split('').reduce(function(a, c) { var code = c.cha ...
- VMware安装最新版CentOS7图文教程
https://blog.csdn.net/reticent_man/article/details/80732395 https://blog.csdn.net/q2158798/article/d ...
- 鸿蒙内核源码分析(内存映射篇) | 虚拟内存虚在哪里 | 百篇博客分析OpenHarmony源码 | v15.03
百篇博客系列篇.本篇为: v15.xx 鸿蒙内核源码分析(内存映射篇) | 虚拟内存虚在哪里 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有哪些分 ...
- 使用 WPF + Chrome 内核实现 在线客服系统 的复合客服端程序
本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程.本产品已经成熟稳定并投入商用. 免费使用 & 私有化部署免费下载:https://docs.sh ...
- 使用python -m pip install 和 pip install 安装包有什么区别?
如果去看源码的话,你会发现 pip 作为执行文件的入口点是 pip._internal.main. 另一方面,pip 作为模块运行时入口是 _main.py,而该模块也只是调用 pip.interna ...
- 3D渲染
3d渲染的本质是在三维坐标系中绘制很三角形,用三角形拼成物体,然后投影到二维图像上,所以做渲染就是画好这些三角形;3d渲染输入的是很多三角形的3个顶点和属性,输出的是一张2d图.画好三角形要研究给三角 ...
- HashMap、ConcurrentHashMap红黑树实现分析
本文学习知识点 1.二叉查找树,以及二叉树查找带来的问题. 2.平衡二叉树及好处. 3.红黑树的定义及构造. 4.ConcurrentHashMap中红黑树的构造. 在正式分析红黑树之前,有必要了解红 ...