【ybtoj】【luogu】【BFS】【例题5】电路维修

【例题5】电路维修


Link

传送门luogu
传送门ybtoj
题目


解题思路

ybtoj的数据太水了,方法一luogu那根本过不去

n n n * m m m的格子图,换成 ( n + 1 ) (n+1) (n+1) * ( m + 1 ) (m+1) (m+1)的点图(?反正就是一个格子四个点)
一个点连向其他点有四种路
在这里插入图片描述

一个点到另一个点的路,假设和初始路不同,边权为1,相同为0

在这里插入图片描述
比如这个就是, l e n [ i ] [ j + 1 ] [ 3 ] = 1 len[i][j+1][3] = 1 len[i][j+1][3]=1 ( i , j + 1 ) (i,j+1) (i,j+1)走到 ( i + 1 , j ) (i+1,j) (i+1,j)路为3,但是此条边是0或2
所以 ( i , j + 1 ) (i,j+1) (i,j+1)走路3到(i+1,j) 的边权为1(即 l e n [ i ] [ j + 1 ] [ 3 ] = 1 len[i][j+1][3] = 1 len[i][j+1][3]=1)
其他同理

从左上角开始走最短路,走到右下角
普通最短路一定会T掉qwq
据luogu大佬博客所说:dijstra+堆优化、SPFA+…一堆我看不懂不知道的东西,是可以过得
但是,蒟蒻选择打 广搜 + 双端队列


双端队列
普通的队列是从队尾插入的,队头弹出的
但是双端队列可以从队头插入,对尾弹出

  • 定义
#include <queue> 

using namespace std;

deque<int>q
  • 队头
q.push_front(a); //队头插入
q.front(); //队头
q.pop_front(); //弹出队头
  • 队尾
q.push_back(b); //队尾插入
q.back(); //队尾
q.pop_back(); //弹出队尾
  • 其他
q.size() //返回队列长度
q.empty(); //如果队列为空,返回true,否则返回false
q.clear(); //清空队列

过程中需要加入最短路松弛
则判断能不能入队时,除了判断当前点在不在队列中,还必须是可以松弛(这样入队后才可能会对答案有贡献)
如果先做优的点,松弛出来的边就可能更优,松弛操作和入队操作就可能变少(就是操作变少,速度变快)

欸,这个时候双端队列就有作用了
两点之间边权为0,则将点从队头入队;两点之间边权为1,则将点从队尾入队
尽量做边权为0的边,松弛可能更优


Code

#include <iostream>
#include <utility>
#include <cstring> 
#include <cstdio>
#include <queue> 
#include <map>

using namespace std;

const int way[4][2] = {
    
    {
    
    -1, -1}, {
    
    -1, 1}, {
    
    1, 1}, {
    
    1, -1}};
pair<int, int> now;
deque<pair<int, int> >q;
int T, n, m, xx, yy, dis[510][510], v[510][510], len[510][510][4];
char c;

void init() {
    
    
	memset(v, 0, sizeof(v));
	memset(len, 0, sizeof(len));
	memset(dis, 0x7f, sizeof(dis));
	q.clear();
} 

bool check(int x, int y) {
    
     return (x > 0 && x <= n && y > 0 && y <= m); }

void BFS() {
    
    
	v[1][1] = 1;
	dis[1][1] = 0;
	q.push_front(make_pair(1, 1));
	while(!q.empty()) {
    
    
		now = q.front();
		v[now.first][now.second] = 0;
		q.pop_front();
		for (int i = 0; i < 4; i++) {
    
    
			xx = now.first + way[i][0], yy = now.second + way[i][1];
			if (!check(xx, yy)) continue;
			if (!len[now.first][now.second][i]) {
    
    //不需要翻转(边权为0)
				if (dis[xx][yy] > dis[now.first][now.second]) {
    
    //松弛
					dis[xx][yy] = dis[now.first][now.second];
					if (!v[xx][yy]) {
    
    
						q.push_front(make_pair(xx, yy));//从队头加入
						v[xx][yy] = 1;
					}
				}
			} else {
    
    
				if (dis[xx][yy] > dis[now.first][now.second] + 1) {
    
    
					dis[xx][yy] = dis[now.first][now.second] + 1;
					if (!v[xx][yy]) {
    
    
						q.push_back(make_pair(xx, yy));//边权为1从队尾加入
						v[xx][yy] = 1;
					}
				}
			}
		}
	}
	if (dis[n][m] != dis[0][0])
		printf("%d\n", dis[n][m]);
	else
		printf("NO SOLUTION\n");
}

int main() {
    
    
//	scanf ("%d", &T);
//	while (T--) {
    
    
		scanf ("%d%d", &n, &m);
		//init();//luogu里是一组数据,ybtoj里是多组数据
		memset(dis, 0x7f, sizeof(dis));
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= m; j++) {
    
    
				cin >> c;
				if (c == '/')
					len[i][j][2] = len[i + 1][j + 1][0] = 1;//需要翻转
				else
					len[i][j + 1][3] = len[i + 1][j][1] = 1;
			}
		n++, m++;
		BFS(); 
//	}
}

猜你喜欢

转载自blog.csdn.net/qq_39940018/article/details/113047265