电路维修 知识点:边权为0,1的图用双端队列bfs求最短路

48 阅读2分钟

P4667 [BalticOI 2011 Day1] Switch the Lamp On - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题意解析

题目是让我们求进行最小操作数,使得电源到正负极的路径最短。 image.png

观察图我们可以很容易发现,只需要把4号边改变一下就可以让电源和正负极连通: image.png image.png

因此我们可以发现每条边要么不变,直接走过去吗,那这条边的权值就是0:

image.png 要么改变,那它的权值就是1:

image.png

解题思路

这道题我们可以看做求从左上角走到右下角的最短路问题。

只不过边权有1和0两个值。

对于非负边权的图,我们可以用dijkstr求最短路。

在这个图中有一半的点我们是无论如何都达到不了的(圈红色的点):

image.png

本题存在无解情况,如果横纵坐标是偶数的话那么这个点一定到达不了,本题无解,输出"NO SOLUTION"。

对于边权是0和1的bfs,我们用双端队列来写,如果扩展出来的边权是1就插到队头,否则就插到队尾。

偏移量问题

因为只能走斜线,且每条斜线在必然在一个格子中,所以我们只能走对角线的四个点:

image.png

我们可以用偏移量把这个点的坐标保存下来:

dx[]={-1,-1,1,1},dy[]={-1,1,1,-1};

除了要保存点外,我们需要把每条边的边权保存下来,

code

#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N = 510, M = N * N;
typedef pair<int, int>PII;
PII q[M];
bool st[N][N];
char g[N][N];
int dist[N][N];
int n, m;


int dx[] = { -1,-1,1,1 }, dy[4] = { -1,1,1,-1 };
int ix[] = {-1,-1,0,0}, iy[] = {-1,0,0,-1};
int bfs()
{
	deque<PII>q;
	char cs[5] = "\\/\\/";

	memset(dist, 0x3f, sizeof dist);
	memset(st, 0, sizeof st);
   
	q.push_back({ 0,0 });
	dist[0][0] = 0;


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

		int x = t.x, y = t.y;
		if (x == n && y == m)return dist[x][y];

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

		for (int i = 0; i < 4; i++)
		{
			int tx = x+dx[i], ty = y+dy[i];
			if (tx <0||tx>n||ty<0||ty>m)continue;
			int ga = x + ix[i], gb = y + iy[i];
			int w = (g[ga][gb]!=cs[i]);
			int d = dist[x][y] +w;
			
			if (d <= dist[tx][ty])
			{
				dist[tx][ty] = d;
				if (!w)q.push_front({ tx,ty });
				else q.push_back({ tx,ty });
			}
				
		}

	}
	return -1;
}
int main()
{

		cin >> n >> m;

		for (int i = 0; i < n; i++)cin >> g[i];
		

		if (n + m & 1)cout << "NO SOLUTION" << endl;   //终点是奇点的话,无解	
		else cout << bfs() << endl;
	

	return 0;
}

image.png