• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
jacklee404
Never Stop!
博客园    首页    新随笔    联系   管理    订阅  订阅
双端队列BFS-电路维修

双端队列BFS

​ 对于图中只存在边权为\(0, 1\)的边,我们可以考虑双端队列优化Dijkstra, 即我们用一个双端队列来维护Dijkstra中堆的性质,具体做法如下:

​ 如果当前更新边的边权为\(0\),那么我们可以将其插入到队首

​ 如果边权为\(1\)我们可以将其插入到队尾

对于上面的做法, 我们可以得到一个BFS序列,证明双端队列合法等同于证明取出的数一定是最小的数,我们可以用归纳法证明:

​ 假设当前的队列为

\[x,x,x,x,\dots,x+1,x+1,x+1,x+1\dots \]

​ 我们将\(x\)取出,那么它生成的\(x + 1\)将会到队尾,\(x\)将会到队头, 显然当\(x + 2\)出现时队列中不存在\(x\), 所以我们可以保证BFS序列的单调性和二段性,也就能够保证取出的数一定时最小的数。

​ 双端队列BFS将Dijkstra算法优化到了\(O(m)\)

经典例题

电路维修

​ 我们可以将每次改变线路置为边权\(1\), 不交换则边权为\(0\), 那么我们便可以在上面跑Dijkstra来求到\((0, 0)->(r, c)\)的最短路,对于网格图来说,点数等于\(N = (r + 1) \times (c + 1)\), 边数可以当成\(M = nm \times 4\)算, 复杂度则为\(O(TMlogN)\), 但是由于存在5个样例,可能会超时,但是由于边权只存在\(0\)和\(1\),因此我们可以使用双端队列来优化。

​ 另外这里处理的时候应该将格子和点分开讨论,具体如下

image-20230412090826184

​ 我们从\((1, 1)可以向(0, 0), (0, 2), (2, 2), (2, 0)\)方向走, 对应的格子转移应该为\([1, 1], [1, 2], [2, 2], [2, 1]\),因此我们的\(dx = [-1, -1, 1, 1], dy = [-1, 1, 1, -1]\), 对应的格子转移\(ix = [0, 0, 1, 1], iy[] = [0, 1, 1, 0]\), 对应的转移字符数组为g = "\\/\\/", 这里需要对\转义, 复杂度就变为了\(O(Tm)\)

Code

#include <iostream>
#include <deque>
#include <cstring>
#define ff first
#define ss second

using i64 = long long;

int r, c, dist[600][600];

char a[600][600];

int dx[] = {-1, -1, 1, 1}, dy[] = {-1, 1, 1, -1};
int ix[] = {0, 0, 1, 1}, iy[] = {0, 1, 1, 0};

bool st[600][600];

/*
	考虑每次交换边权为1, 那么问题转化为从(1, 1) 到 (n, m) 的最短路

	因为边权只有0和1,所以我们可以考虑双端队列优化的dijkstra

	基本原理为, 将堆 改为 双端队列,插入时如果边权为1,那么把更新的点插入到队尾, 如果边权为0把更新的点插入到队头

	保证了每次出堆的都是离源点最近的点
*/

int bfs() {
	memset(dist, 0x3f, sizeof dist);
	memset(st, false, sizeof st);

	std::deque<std::pair<int, int>> q1;

	q1.push_front({0, 0});

	dist[0][0] = 0;

	while (q1.size()) {
		auto t = q1.front(); q1.pop_front();

		int x = t.ff, y = t.ss;

		if (st[x][y])
			continue;

		st[x][y] = true;

		char g[5] = "\\/\\/";
	
		for (int i = 0; i <= 3; i ++) {
			int tx = x + dx[i], ty = y + dy[i];

			if (tx < 0 || ty < 0 || tx > r || ty > c) continue;

			int gx = x + ix[i], gy = y + iy[i];

			int v = !(a[gx][gy] == g[i]);

			if (dist[tx][ty] > dist[x][y] + v) {
				dist[tx][ty] = dist[x][y] + v;
				if (v) {
					q1.push_back({tx, ty});
				} else {
					q1.push_front({tx, ty});
				}
			}
		}
	}

	return dist[r][c] == 0x3f3f3f3f?-1:dist[r][c];
}

void solve() {
	std::cin >> r >> c; 

	for (int i = 1; i <= r; i ++) {
		for (int j = 1; j <= c; j ++) {
			std::cin >> a[i][j];
		}
	}

	if (r + c & 1) {
		puts("NO SOLUTION");
	} else {
		int k = bfs();

		if (k == -1) {
			puts("NO SOLUTION");
		} else std::cout << k << "\n";
	}
}

int main() {
	int _;

	std::cin >> _;

	while (_ --) {
		solve();
	}
}
posted on 2023-04-12 09:14  Jack404  阅读(34)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3