CodeForces-1301F Super Jaber 【多源BFS】⭐

Super Jaber

题意: 在一个n*m的二维平面中,每一个方格都有特定的颜色。方格中的移动规则为纵向或横向移动一个单位耗费一个时间,从一个颜色为i的方格跳转向另一个相同颜色的方格耗费一个时间。求从某一个方格到目标方格的最小时间。

思路: 首先看题目的一些数据范围:时间限制time limit per test5 seconds、二维平面的大小和颜色范围1 ≤ n, m ≤ 1000 , 1 ≤ k ≤ min(40, n ⋅ m)、以及q次询问1 ≤ q ≤ 1 0 5 10^5 105。可见事情的严重性了,各种数据范围都非常大,包括时间限制也给足了五秒。

  • 求出全图的最短路,每次询问直接调用答案即可。( 1 0 5 10^5 105次询问,换谁都不敢问一次求一次的吧?【小声)
  • 枚举颜色求出最短路。最多可达 1 0 6 10^6 106个方格,如果枚举方格进行求最短路,又是一个恐怖的数组。

找到了切入口,那该怎么对颜色下手呢?一个很重要的点在于相同颜色的方格之间的跳转只需要一个单位时间,所以我们不一定非要记录两个方格之间的最短距离,而是计算某个方格到某种颜色的最短距离,这样两个方格之间的最短距离就是就是彼此到达某种颜色的最短距离之和+1. 思考:

  • 定义dis[color][x][y] 表示坐标x、y的方格到达颜色color的最短距离。
  • 通过枚举每一种颜色,用BFS求出到达所有方格的最短路。
  • {x1, y1}到{x2, y2}的最短距离= min(dis[i][x1][y1] + dis[color][x2][y2] + 1: 容易疑惑的是为什么需要加一,因为手段有限(其实就是懒得画图),这个问题留给读者自己思考,其实只要模拟一遍就很明了了。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
#define fi first
#define se second

int n, m, k, q;
int G[N][N];
int dis[41][N][N];
vector<pair<int, int> > color[41];//fi->x, se->y
void bfs(int c) {
	int vis[41] = {0};
	queue<pair<int, int> > Q;
	for(int i = 0; i < color[c].size(); i++) {
		int x = color[c][i].fi, y = color[c][i].se;
		dis[c][x][y] = 0;
		Q.push({x, y});
	}
	vis[c] = 1;
	while(!Q.empty()) {
		int sx = Q.front().fi, sy = Q.front().se; Q.pop();
		int sc = G[sx][sy], sdis = dis[c][sx][sy];
		if(!vis[sc]) {
			vis[sc] = 1;
			for(int i = 0; i < color[sc].size(); i++) {
				int ex = color[sc][i].fi, ey = color[sc][i].se;
				if(dis[c][ex][ey] != -1) continue;
				dis[c][ex][ey] = sdis + 1;
				Q.push({ex, ey});	
			}
		}
		int dir[][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
		for(int i = 0; i < 4; i++) {
			int ex = sx + dir[i][0];
			int ey = sy + dir[i][1];
			if(ex < 1 || ex > n || ey < 1 || ey > m || dis[c][ex][ey] != -1)	continue;
			dis[c][ex][ey] = sdis + 1;
			Q.push({ex, ey});
		}
	}
}
int main() {
	scanf("%d%d%d", &n, &m, &k);
	for(int i = 1; i <= n; i++)	
		for(int j = 1; j <= m; j++) {
			scanf("%d", G[i] + j);
			color[G[i][j]].push_back({i, j});
		}
	memset(dis, -1, sizeof dis);
	for(int i = 1; i <= k; i++)	bfs(i);
	scanf("%d", &q);
	while(q--) {
		int x1, y1, x2, y2;
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		int ans = abs(x1 - x2) + abs(y1 - y2);
		for(int i = 1; i <= k; i++)	ans = min(ans, dis[i][x1][y1] + dis[i][x2][y2] + 1);	//+1?
		printf("%d\n", ans);
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值