AT_abc413_g 题解
这道题其实没有想象中那么难,赛后花了1h+ AC掉了,赛事没看(T^T)
题目传送门
vjudge AtCoder413 G - Big Banned Grid (+中文翻译)
AtCoder 413 G - Big Banned Grid
题面
有一个 \(H*W\) 的巨大网格,上面有 \(K\) 个障碍物,高桥在上面按照以下规则从 \((1,1)\) 走路 \((H, W)\)。
- 高桥每次可以往上下左右任意一个方向走动,但是不能出界也不能穿过障碍物。
请问高桥能否从 \((1,1)\) 成功抵达 \((H,W)\)
温馨提示:
\(1 \leq H,W \leq 2*10^5\)
\(0 \leq K \leq 2*10^5\)
思路
看到 \(H,W \leq 2*10^5\) 就知道没法DFS / BFS,地图都存不了。
但是 \(K \leq 2*10^5\),\(K\)不大!
继续观察:发现如果 \(K\) 个障碍中的某几个形成了一堵很长的墙,将高桥堵住了,那么高桥就不可以到达 \((H,W)\) 了。
继续观察:发现这堵墙一定是从左边/下面 延伸到 右边/上面,不然堵不住!
那么这道题就好办了,对于每一个障碍,如果它是靠着左边/下面,那么标记为1,如果它靠着右边/上面,那么标记为2,然后任何一个标记为1的障碍若可以连通到标记为2的障碍,那么就形成了一堵可以阻挡高桥的旅行的长墙!
那怎么看连通呢?
利用map记住每一个障碍的输入的下标位置,然后用iterator遍历一遍这个map,然后从iterator->first对应的位置向周围看有没有与这个障碍连通的,如果有,就用并查集合并成同一堵墙。
最后看有没有一堵墙既有标记为1的点,又有标记为2的点。
注意:
1、因为高桥只能上下左右走,不能斜线走,所以墙只要八连通就可以了,不需要四连通。
2、如果\(H = 1\) 或者 \(W = 1\) ,那么1个障碍可以同时靠着左边/下面和右边/上面,直接成为一堵堵住高桥的墙。
代码:
#include <bits/stdc++.h>
using namespace std;
map<pair<int, int>, int> mp;
int dr[8] = { -1, -1, -1, 0, 0, 1, 1, 1 }; // 八个方向
int dc[8] = { -1, 0, 1, -1, 1, -1, 0, 1 }; // 八个方向
int fa[200001]; // 并查集的fa数组
int flag[200001];// 每一个点的标记
pair<int, int> cnt[200001]; // 统计每一堵墙的两个标记的数量
int findfa(int a) // 并查集找根
{
if (fa[a] == a)
{
return a;
}
fa[a] = findfa(fa[a]);
return fa[a];
}
void Union(int a, int b) // 并查集合并
{
int afa = findfa(a);
int bfa = findfa(b);
fa[afa] = bfa;
}
int main()
{
int h, w, k;
cin >> h >> w >> k;
for (int i = 1; i <= k; i++)
{
fa[i] = i; // 别忘了给 fa 初始化
int r, c;
cin >> r >> c;
mp[ { r, c } ] = i; // 记住该障碍的下标
if (r == h || c == 1) // 在左边/下面
{
flag[i] = 1;
}
if (r == 1 || c == w) // 在右边/上面
{
flag[i] = 2;
}
if ((r == h || c == 1) && (r == 1 || c == w)) // 两个都是
{
flag[i] = 3;
}
}
for (map<pair<int, int>, int>::iterator it = mp.begin(); it != mp.end(); it++) // 对于每一个障碍
{
int r = it->first.first;
int c = it->first.second;
int id = it->second;
for (int i = 0; i < 8; i++) // 找八连通的点
{
int nr = r + dr[i];
int nc = c + dc[i];
if (mp.count( { nr, nc } )) // 如果存在
{
int nid = mp[ { nr, nc } ];
Union(id, nid); // 合并成同一个墙
}
}
}
for (int i = 1; i <= k; i++)
{
int f = findfa(i); // 只要对并查集中的该集的根进行操作即可,因为根代表着整个集合
cnt[f].first += (flag[i] == 1 || flag[i] == 3);
cnt[f].second += (flag[i] == 2 || flag[i] == 3);
}
for (int i = 1; i <= k; i++)
{
if (cnt[i].first && cnt[i].second) // 如果形成了一堵贯穿左/下和右/上的长墙,那么高桥就无法通过
{
cout << "No" << endl;
return 0;
}
}
cout << "Yes" << endl; // 否则高桥就可以通过!
return 0;
}

浙公网安备 33010602011771号