多源BFS-双端队列广搜

多源BFS

AcWing173. 矩阵距离

给定一个N行M列的01矩阵A, A [ i ] [ j ] A[i][j] A[i][j] A [ k ] [ l ] A[k][l] A[k][l] 之间的曼哈顿距离定义为:

d i s t ( A [ i ] [ j ] , A [ k ] [ l ] ) = ∣ i − k ∣ + ∣ j − l ∣ dist(A[i][j],A[k][l])=|i−k|+|j−l| dist(A[i][j],A[k][l])=ik+jl

输出一个N行M列的整数矩阵B,其中: B [ i ] [ j ] = m i n 1 ≤ x ≤ N , 1 ≤ y ≤ M , A [ x ] [ y ] = 1 d i s t ( A [ i ] [ j ] , A [ x ] [ y ] ) B[i][j]=min_{1≤x≤N,1≤y≤M,A[x][y]=1}dist(A[i][j],A[x][y]) B[i][j]=min1xN,1yM,A[x][y]=1dist(A[i][j],A[x][y])

输入格式

第一行两个整数n,m。

接下来一个N行M列的01矩阵,数字之间没有空格。

输出格式

一个N行M列的矩阵B,相邻两个整数之间用一个空格隔开。

数据范围

1 ≤ N , M ≤ 1000 1≤N,M≤1000 1N,M1000

代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<string>
#include<cstring>
#include<map>
#include<vector>
#include<set>
#include<stack>
#include<algorithm>
#include<vector>
#include<utility>
#include<deque>
#include<unordered_map>
#define INF 0x3f3f3f3f
#define mod 1000000007
#define endl '\n'
#define eps 1e-6
inline int gcd(int a, int b) {
    
     return b ? gcd(b, a % b) : a; }
inline int lowbit(int x) {
    
     return x & -x; }


using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 1010;

int n, m;
int dist[N][N];
char g[N][N];

void bfs() {
    
    
	memset(dist, -1, sizeof dist);
	queue<PII>q;
	for (int i = 0; i < n; ++i) {
    
    
		for (int j = 0; j < m; ++j) {
    
    
			if (g[i][j] == '1') {
    
    
				q.push({
    
     i,j });
				dist[i][j] = 0;
			}
		}
	}
	int dx[4] = {
    
     0,1,0,-1 }, dy[4] = {
    
     1, 0, -1, 0 };

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

		for (int i = 0; i < 4; ++i) {
    
    
			int a = t.first + dx[i];
			int b = t.second + dy[i];

			if (a < 0 || a >= n || b < 0 || b >= m)continue;
			if (dist[a][b] != -1)continue;

			dist[a][b] = dist[t.first][t.second] + 1;
			q.push({
    
     a,b });
		}
	}

}
int main() {
    
    
	scanf("%d%d", &n, &m);

	for (int i = 0; i < n; ++i)scanf("%s", g[i]);

	bfs();
	for (int i = 0; i < n; ++i) {
    
    
		for (int j = 0; j < m; ++j) {
    
    
			printf("%d ", dist[i][j]);
		}
		puts("");
	}
	return 0;
}

最小步数模型

AcWing1107. 魔板

Rubik 先生在发明了风靡全球的魔方之后,又发明了它的二维版本——魔板。

这是一张有 8 个大小相同的格子的魔板:

1 2 3 4
8 7 6 5

我们知道魔板的每一个方格都有一种颜色。

这 8 种颜色用前 8 个正整数来表示。

可以用颜色的序列来表示一种魔板状态,规定从魔板的左上角开始,沿顺时针方向依次取出整数,构成一个颜色序列。

对于上图的魔板状态,我们用序列 (1,2,3,4,5,6,7,8) 来表示,这是基本状态。

这里提供三种基本操作,分别用大写字母 A,B,C 来表示(可以通过这些操作改变魔板的状态):

A:交换上下两行;
B:将最右边的一列插入到最左边;
C:魔板中央对的4个数作顺时针旋转。

下面是对基本状态进行操作的示范:

A:

8 7 6 5
1 2 3 4

B:

4 1 2 3
5 8 7 6

C:

1 7 2 4
8 6 3 5

对于每种可能的状态,这三种基本操作都可以使用。

你要编程计算用最少的基本操作完成基本状态到特殊状态的转换,输出基本操作序列。

注意:数据保证一定有解。

输入格式

输入仅一行,包括 8 个整数,用空格分开,表示目标状态。

输出格式

输出文件的第一行包括一个整数,表示最短操作序列的长度。

如果操作序列的长度大于0,则在第二行输出字典序最小的操作序列。

数据范围

输入数据中的所有数字均为 1 到 8 之间的整数。

代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<string>
#include<cstring>
#include<map>
#include<vector>
#include<set>
#include<stack>
#include<algorithm>
#include<vector>
#include<utility>
#include<deque>
#include<unordered_map>
#define INF 0x3f3f3f3f
#define mod 1000000007
#define endl '\n'
#define eps 1e-6
inline int gcd(int a, int b) {
    
     return b ? gcd(b, a % b) : a; }
inline int lowbit(int x) {
    
     return x & -x; }


using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;

char g[2][4];
unordered_map<string, int> dist;
unordered_map<string, pair<char, string>>pre;

void set_(string state) {
    
    
	for (int i = 0; i < 4; ++i) {
    
    
		g[0][i] = state[i];
	}

	for (int i = 3, j = 4; i >= 0; --i, ++j) {
    
    
		g[1][i] = state[j];
	}
}

string get_() {
    
    
	string res;
	for (int i = 0; i < 4; ++i) {
    
    
		res += g[0][i];
	}

	for (int i = 3; i >= 0; --i) {
    
    
		res += g[1][i];
	}

	return res;
}

string move0(string state) {
    
    
	set_(state);

	for (int i = 0; i < 4; ++i)swap(g[0][i], g[1][i]);

	return get_();
}

string move1(string state) {
    
    
	set_(state);

	char t[2];
	t[0] = g[0][3];
	t[1] = g[1][3];
	for (int i = 3; i >= 0; --i) {
    
    
		for (int j = 0; j < 2; ++j) {
    
    
			g[j][i] = g[j][i - 1];
		}
	}

	g[0][0] = t[0];
	g[1][0] = t[1];
	return get_();
}



string move2(string state){
    
    
	set_(state);

	char t = g[0][1];
	g[0][1] = g[1][1];
	g[1][1] = g[1][2];
	g[1][2] = g[0][2];
	g[0][2] = t;
	return get_();
}


void bfs(string start, string end) {
    
    
	queue<string>q;
	q.push(start);
	if (start == end)return;
	while (q.size()) {
    
    
		auto t = q.front();
		q.pop();

		string m[3];
		m[0] = move0(t);
		m[1] = move1(t);
		m[2] = move2(t);

		for (int i = 0; i < 3; ++i) {
    
    
			string str = m[i];
			if (dist.count(str) == 0) {
    
    
				
				dist[str] = dist[t] + 1;
				pre[str].first = char(i + 'A');
				pre[str].second = t;
				if (str == end)return;
				q.push(str);
			}
		}
	}
}
int main() {
    
    
	string end, start;
	for (int i = 0; i < 8; ++i) {
    
    
		int c; cin >> c;
		end += char(c + '0');
	}

	for (int i = 0; i < 8; ++i) {
    
    
		start += char(i + '1');
	}

	bfs(start, end);
	cout << dist[end] << endl;
	
	string res;
	while (end != start) {
    
    
		res += pre[end].first;
		end = pre[end].second;
	}

	reverse(res.begin(), res.end());

	if (res.size())cout << res << endl;
	return 0;
}

双端队列广搜(0-1 BFS)

适用于边权只有0和1的图

AcWing175. 电路维修

思路

只能通过偶点(横纵坐标和为偶数的点)


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

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

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

电路板的整体结构是一个R行C列的网格(R,C≤500),如下图所示。

电路.png

每个格点都是电线的接点,每个格子都包含一个电子元件。

电子元件的主要部分是一个可旋转的、连接一条对角线上的两个接点的短电缆。

在旋转之后,它就可以连接另一条对角线的两个接点。

电路板左上角的接点接入直流电源,右下角的接点接入飞行车的发动装置。

达达发现因为某些元件的方向不小心发生了改变,电路板可能处于断路的状态。

她准备通过计算,旋转最少数量的元件,使电源与发动装置通过若干条短缆相连。

不过,电路的规模实在是太大了,达达并不擅长编程,希望你能够帮她解决这个问题。

注意:只能走斜向的线段,水平和竖直线段不能走。

输入格式

输入文件包含多组测试数据。

第一行包含一个整数T,表示测试数据的数目。

对于每组测试数据,第一行包含正整数R和C,表示电路板的行数和列数。

之后R行,每行C个字符,字符是"/""\"中的一个,表示标准件的方向。

输出格式

对于每组测试数据,在单独的一行输出一个正整数,表示所需的缩小旋转次数。

如果无论怎样都不能使得电源和发动机之间连通,输出NO SOLUTION。

数据范围

1 ≤ R , C ≤ 500 1≤R,C≤500 1R,C500
1 ≤ T ≤ 5 1≤T≤5 1T5

代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<string>
#include<cstring>
#include<map>
#include<vector>
#include<set>
#include<stack>
#include<cmath>
#include<algorithm>
#include<vector>
#include<utility>
#include<deque>
#define INF 0x3f3f3f3f
#define INFL 0x3f3f3f3f3f3f3f3f
#define mod 1000000007
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define endl '\n'
#define eps 1e-6
inline int gcd(int a, int b) {
    
     return b ? gcd(b, a % b) : a; }
inline int lowbit(int x) {
    
     return x & -x; }


using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<int, PII>PIII;
const int N = 510;
int n, m;
PII p[N];
char g[N][N];
int dist[N][N];
bool vis[N][N];

int bfs() {
    
    
	memset(dist, 0x3f, sizeof dist);
	memset(vis, 0, sizeof vis);
	deque<PII>q;
	
    dist[0][0] = 0;
	q.push_back({
    
     0,0 });

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

		int x = t.first, y = t.second;
		if (x == n && y == m)return dist[x][y]; //找到终点直接返回
        vis[x][y] = true;
		
		int dx[4] = {
    
     -1,-1,1,1 }, dy[4] = {
    
     -1,1,1,-1 }; //枚举四个方向
		int ix[] = {
    
     -1,-1,0,0 }, iy[] = {
    
     -1,0,0,-1 }; //线路于当前点的相对位置

		char ok[5] = "\\/\\/"; //不用移动的电路状态

		for (int i = 0; i < 4; ++i) {
    
    // 遍历四个方向
			int a = x + dx[i]; 
			int b = y + dy[i];

			if (a < 0 || a > n || b < 0 || b > m)continue;
			if (vis[a][b])continue;

			int ga = x + ix[i], gb = y + iy[i]; //找到电线在图中的位置
			int w = g[ga][gb] != ok[i]; //将当前电线与当前点接通的花费
			int d = dist[x][y] + w; //从队头转移到当前点的花费
			if (d <= dist[a][b]) {
    
     //如果小于等于 则更新
				dist[a][b] = d;
				if (!w)q.push_front({
    
     a,b }); //边权为0 插入队头
				else q.push_back({
    
     a,b }); //边权为1 插入队尾
			}
		}
	}
	return -1;
}


int main() {
    
    
	int t; cin >> t;
	while (t--) {
    
    
		scanf("%d%d", &n, &m);
		for (int i = 0; i < n; ++i)scanf("%s", g[i]);

		if (n + m & 1)puts("NO SOLUTION");
		else printf("%d\n", bfs());
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zzq0523/article/details/109251804