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

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

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

电路板的整体结构是一个$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;
}

电路维修(双端队列 & 最短路)的更多相关文章

  1. CH 2601 - 电路维修 - [双端队列BFS]

    题目链接:传送门 描述 Ha'nyu是来自异世界的魔女,她在漫无目的地四处漂流的时候,遇到了善良的少女Rika,从而被收留在地球上.Rika的家里有一辆飞行车.有一天飞行车的电路板突然出现了故障,导致 ...

  2. Luogu P2243 电路维修 双端队列BFS

    当转移的代价是0和一个分明不同的权值时,可以用双端队列BFS去跑(你跑最短路也没问题..QWQ) 而对于这道题,边旋转代价是1,不旋转代价是0:可以直接建图最短路,也可以跑BFS 这个题建图很有意思: ...

  3. POJ3662 SPFA//二分 + 双端队列最短路

    https://cn.vjudge.net/problem/12427/origin 题意:求1到N第K + 1大条边权最小的路径 首先想到dp递推,dp[x][y]表示到x这个点经过y条免费边的最小 ...

  4. CH2601 电路维修(双端队列bfs)建图恶心

    CH2601 电路维修 双端队列bfs,其实就是因为只有0和1所以可以直接2维护队列单调性(和优先队列一个道理) 建图的过程需要仔细斟酌(想一想id为什么这么写) 还有,空间要开够(很玄学),我一开始 ...

  5. 2601 电路维修 (双端队列bfs\优先队列bfs(最短路))

    描述 Ha'nyu是来自异世界的魔女,她在漫无目的地四处漂流的时候,遇到了善良的少女Rika,从而被收留在地球上.Rika的家里有一辆飞行车.有一天飞行车的电路板突然出现了故障,导致无法启动. 电路板 ...

  6. 电路维修 (广搜变形-双端队列bfs)

    # 2632. 「BalticOI 2011 Day1」打开灯泡 Switch the Lamp On [题目描述] 有一种正方形的电路元件,在它的两组相对顶点中,有一组会用导线连接起来,另一组则不会 ...

  7. POJ 3662 Telephone Lines【二分答案+最短路】||【双端队列BFS】

    <题目链接> 题目大意: 在一个节点标号为1~n的无向图中,求出一条1~n的路径,使得路径上的第K+1条边的边权最小. 解题分析:直接考虑情况比较多,所以我们采用二分答案,先二分枚举第K+ ...

  8. P - The Shortest Path in Nya Graph-hdu4725(双端队列+拆点)

    题意:有N个点和N层..一层有X个点(0<=X<=N).两邻两层间有一条路花费C.还有M条小路在两个点之间.问从第一个点走到第N个点最短路是多少... 可以考虑在每一层增加一个点,这个点到 ...

  9. [lougu2243]双端队列搜索

    正统双端队列搜索 回顾:普通队列进行边权为定值的最短路 每次到达都是最优的(意味着不用取min) why? 因为所有状态按照 入队的先后顺序 具有 层次单调性,每次扩展,都往外走一步,满足从起始到该状 ...

随机推荐

  1. mysql触发器实时检测一条语句进行备份删除

    问题描述:用户有一个这样一个需求,在一张表里会不时出现 "违规" 字样的字段,需要在出现这个字段的时候,把整行的数据删掉.这是个采集任务,如果发现有"违规"字样 ...

  2. select后给字段起别名,where和group后不能用,但having后可以

    为什么mysql having的条件表达式可以直接使用select后的别名? SQL语句的语法顺序: FROM -> WHERE -> GROUP BY -> HAVING -> ...

  3. 学习PHP中有趣的字符集国际化验证功能

    今天的内容非常简单,不过也很有趣.不知道大家有没有经历过这样的事情,就是在某些字体下,0 和 O 不好区分,1 和 l 也是很难看清楚.当然,现在大部分的编辑器和 IDE 的默认字体都是会选择那些比较 ...

  4. JavaScript进阶面向对象ES6

    类和对象 对象:万物皆对象,对象是一个具体的事物,看得见摸得着的实物 对象是由属性和方法组成的: 属性:事物的特征,再对象中用属性来表示(常用名词) 方法:事物的行为,再对象中用方法来表示(常用动词) ...

  5. nginx 常用x代码

    1.nginx 禁止ip直接访问,只允许域名访问,直接在.conf文件里 server上面再添加一个server 代码,不可以写同一个server里: server { listen 80 defau ...

  6. django 对models中上传的文件或图片改名

    def user_directory_path(instance,filename): print instance,filename ext = filename.split('.')[-1] pr ...

  7. sunny 内网穿透使用。

    启动方法:

  8. Java面试总结 Boss沟通过:500+,面试:20,已投简历130+

    1 概述 1 介绍 最近换工作,对最近面试的过程进行总结,总结每个公司的面试流程和问到的面试题,记录自己,也供大家参考. 我是一名Java开发,工作经验10年,所以面试一名高级Java开发工程师. 简 ...

  9. Http Only Cookie保护AccessToken

    前言 JWT认证方式目前已被广泛使用,一直以来我们将token放在请求头中的Authorization中,若通过此种方式,一旦token被恶意窃取,攻击者可肆意对用户可访问资源进行任意索取,我们大多都 ...

  10. Go的Select

    Go 的通道有两种操作方式,一种是带 range 子句的 for 语句,另一种则是 select 语句,它是专门为了操作通道而存在的.这里主要介绍 select 的用法. 一.select的语法 se ...