题意:
一个 n∗m n ∗ m 的网格迷宫(会有一些墙),现在迷宫内取 K K 个点,两点之间的距离为迷宫内的最短距离,求要形成这个点的最小生成树(边权即为最短距离),其权值为多少?( n,m≤2000 n , m ≤ 2000 )
思路:
这个题思路很容易想出来,就是多源点的BFS,然后其中套一个Kruskal最小生成树算法就行了。比赛中,只过了65%的test case,哎呀,好弱啊。后来才知道,有个地方疏忽了。
一个很容易出错的地方就是:BFS过程中,添加边的顺序,考虑这种情况:O##O###O,就是三个出发点O,中间#表示待访问点(分别编号为12345)。第一轮BFS完后,队列中会有1235这四个点,按道理来说,我们应该先考虑添加距离最短的边(距离为3),即1->2或者2->1,但是可能会出现先把另外一条边(距离为4)先加进去的情况(当队列访问顺序为5321时),这样就会出现问题,所以这种情况应该分开处理。
详见代码吧,思路很简单,就是模拟的时候容易出错。
代码:
#include <bits/stdc++.h>
#define PB push_back
#define FT first
#define SD second
#define MP make_pair
#define INF 0x7fffffff
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> P;
typedef vector<int> VI;
typedef map<int,int> MII;
const int N = 2e3 + 10, M = 1e5 + 10;
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
int n, m, k;
LL ans;
char LR[N][N], TB[N][N];
P V[M];
typedef struct state
{
int x, y, dis, from;
state(int _x=0, int _y=0, int _from=0, int _dis=0)
{
x = _x, y = _y, from = _from, dis = _dis;
}
}state;
int C, p[M];
int find_set(int x)
{
if(x == p[x]) return x;
return p[x] = find_set(p[x]);
}
int vis[N][N], D[N][N];
void init()
{
C = k, ans = 0;
for(int i = 1;i <= C;i ++) p[i] = i;
memset(vis, -1, sizeof vis);
}
state Q[N*N];
vector<pair<P, P>> Vx;
LL BFS()
{
int cnt = 0, qh = 0, ql = 0;
for(int i = 1;i <= k;i ++)
{
Q[cnt ++] = state(V[i].FT, V[i].SD, i, 0);
vis[V[i].FT][V[i].SD] = i, D[V[i].FT][V[i].SD] = 0;
}
qh = cnt;
while(ql < qh)
{
for(int o = ql;o < qh;o ++)
{
state now = Q[o];
for(int i = 0;i < 4;i ++)
{
int x = now.x + dx[i], y = now.y + dy[i];
if(x < 1 || x > n || y < 1 || y > m) continue;
if(i == 0 && TB[x][y] == '1') continue;
if(i == 1 && TB[now.x][now.y] == '1') continue;
if(i == 2 && LR[x][y] == '1') continue;
if(i == 3 && LR[now.x][now.y] == '1') continue;
if(vis[x][y] == -1)
{
vis[x][y] = now.from, D[x][y] = now.dis + 1;
Q[cnt ++] = state(x, y, now.from, now.dis + 1);
}
else
{
int u = find_set(vis[x][y]), v = find_set(now.from);
if(u == v) continue;
// 容易出错的地方
if(D[x][y] == now.dis)
{
p[u] = v, C --;
ans += D[x][y] + now.dis + 1;
if(C == 1) return ans;
}
else Vx.PB({{x, y}, {now.from, now.dis}});
}
}
}
// 分开处理
for(int i = 0;i < Vx.size();i ++)
{
P t1 = Vx[i].FT, t2 = Vx[i].SD;
int u = find_set(vis[t1.FT][t1.SD]), v = find_set(t2.FT);
if(u != v)
{
p[u] = v, C --;
ans += D[t1.FT][t1.SD] + t2.SD + 1;
if(C == 1) return ans;
}
}
Vx.clear(), ql = qh, qh = cnt;
}
return ans;
}
int main()
{
#ifdef YUUKILP
freopen("in.txt","r",stdin);
#endif
ios_base::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m >> k;
for(int i = 1;i <= n;i ++) cin >> LR[i] + 1;
for(int i = 1;i < n;i ++) cin >> TB[i] + 1;
for(int i = 1;i <= k;i ++)
{
int x, y; cin >> x >> y;
V[i] = MP(x, y);
}
if(k == 1) return cout << 0, 0;
init();
return cout << BFS(), 0;
}
官方代码:
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
const int b13 = 1<<13;
char hmove[int(2e7) + 10];
char vmove[int(2e7) + 10];
int vis[int(2e7) + 10];
int dep[int(2e7) + 10];
int qidx[2000 * 2000 + 10];
int cnt;
int n, m, k;
int ans;
int temp[2000 * 2000 * 2 + 10];
int *temp_a = temp;
int *temp_b = temp + 2000 * 2000 + 5;
inline int check(const int &idx, const int &dir) {
if (dir == 0)
return hmove[idx] ? idx+1 : 0;
else if (dir == 1)
return hmove[idx-1] ? idx-1 : 0;
else if (dir == 2)
return vmove[idx] ? idx+b13 : 0;
else if (dir == 3)
return vmove[idx-b13] ? idx-b13 : 0;
}
int root(const int &x) {
return vis[x] == x ? x : vis[x] = root(vis[x]);
}
int main()
{
char s[10010];
cin >> n >> m >> k;
for (int i = 0; i < n; ++i) {
scanf("%s", s);
for (int j = 0; j < m-1; ++j) {
int is_wall = s[j] == '1';
hmove[(i+1)<<13|(j+1)] = !is_wall;
}
}
for (int i = 0; i < n-1; ++i) {
scanf("%s", s);
for (int j = 0; j < m; ++j) {
int is_wall = s[j] == '1';
vmove[(i+1)<<13|(j+1)] = !is_wall;
}
}
for (int i = 1; i <= k; ++i) {
int idx, x, y;
scanf("%d %d", &x, &y);
idx = x<<13|y;
vis[idx] = idx;
qidx[cnt++] = idx;
}
int hp = 0, tp = cnt, curd = 0;
while (hp < tp)
{
curd ++;
for (int i = hp; i < tp; ++i)
{
int idx = qidx[i];
for (int j = 0; j < 4; ++j)
{
int nidx = check(idx, j);
if (nidx == 0) continue;
if (vis[nidx])
{
if (root(nidx) != root(idx))
if (dep[nidx] == dep[idx])
{
ans += curd;
vis[root(nidx)] = root(idx);
}
else
{
*(temp_a++) = idx;
*(temp_b++) = nidx;
}
}
else
{
vis[nidx] = root(idx);
dep[nidx] = dep[idx] + 1;
qidx[cnt++] = nidx;
}
}
}
curd ++;
while (temp_a != temp)
{
--temp_a, --temp_b;
if (root(*temp_a) != root(*temp_b)) {
vis[root(*temp_b)] = root(*temp_a);
ans += curd;
}
}
hp = tp;
tp = cnt;
}
cout << ans << endl;
}