P4667 [BalticOI 2011 Day1] Switch the Lamp On - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意解析
题目是让我们求进行最小操作数,使得电源到正负极的路径最短。
观察图我们可以很容易发现,只需要把4号边改变一下就可以让电源和正负极连通:
因此我们可以发现每条边要么不变,直接走过去吗,那这条边的权值就是0:
要么改变,那它的权值就是1:
解题思路
这道题我们可以看做求从左上角走到右下角的最短路问题。
只不过边权有1和0两个值。
对于非负边权的图,我们可以用dijkstr求最短路。
在这个图中有一半的点我们是无论如何都达到不了的(圈红色的点):
本题存在无解情况,如果横纵坐标是偶数的话那么这个点一定到达不了,本题无解,输出"NO SOLUTION"。
对于边权是0和1的bfs,我们用双端队列来写,如果扩展出来的边权是1就插到队头,否则就插到队尾。
偏移量问题
因为只能走斜线,且每条斜线在必然在一个格子中,所以我们只能走对角线的四个点:
我们可以用偏移量把这个点的坐标保存下来:
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;
}