题解:P13654 [CERC 2020] Excavation
DreamFairy · · 题解
Prepare
深度优先搜索。
Solution
五种类型的挖掘机不重要,只是相当于五种移动方式而已,本质是一样的。
注意到任意两点间都有双向边,所以本质是一张无向图,每次可以将一条边缩成一个点,求能否将图缩成一个点。
由于是无向图,所以只要这张图连通就一定能缩成一个点,缩成哪个点不重要。我们取任意一个点为最终剩下的点,通过搜索向外扩展并记录路径,只要发现可达点就朝可达点搜索,最后判断一下是否还有未搜索到的点,若有则说明图不连通,无解,否则反向输出路径。反向输出是因为我们刚才是倒推着遍历的。
Code
#include <bits/stdc++.h>
using namespace std;
const long long N = 105, inf = 4e18, mod = 998244353;
long long n, cnt = 0, sx, sy;
struct res {
long long sx, sy, tx, ty;
} ans[N * N];
bool vis[N][N];
char ty, ch;
inline long long read() {
long long x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - 48;
ch = getchar();
}
return x * f;
}
void dfs(long long x, long long y, long long nx, long long ny) {
vis[x][y] = 0;
ans[++cnt] = res{x, y, nx, ny};
if (ty == 'R') {
for (long long i = 1; i <= n; ++i) {
if (x - i >= 1 && vis[x - i][y]) {
dfs(x - i, y, x, y);
}
if (x + i <= n && vis[x + i][y]) {
dfs(x + i, y, x, y);
}
if (y - i >= 1 && vis[x][y - i]) {
dfs(x, y - i, x, y);
}
if (y + i <= n && vis[x][y + i]) {
dfs(x, y + i, x, y);
}
}
}
if (ty == 'Q') {
for (long long i = 1; i <= n; ++i) {
if (x - i >= 1 && vis[x - i][y]) {
dfs(x - i, y, x, y);
}
if (x + i <= n && vis[x + i][y]) {
dfs(x + i, y, x, y);
}
if (y - i >= 1 && vis[x][y - i]) {
dfs(x, y - i, x, y);
}
if (y + i <= n && vis[x][y + i]) {
dfs(x, y + i, x, y);
}
if (x - i >= 1 && y - i >= 1 && vis[x - i][y - i]) {
dfs(x - i, y - i, x, y);
}
if (x - i >= 1 && y + i <= n && vis[x - i][y + i]) {
dfs(x - i, y + i, x, y);
}
if (x + i <= n && y - i >= 1 && vis[x + i][y - i]) {
dfs(x + i, y - i, x, y);
}
if (x + i <= n && y + i <= n && vis[x + i][y + i]) {
dfs(x + i, y + i, x, y);
}
}
}
if (ty == 'B') {
for (long long i = 1; i <= n; ++i) {
if (x - i >= 1 && y - i >= 1 && vis[x - i][y - i]) {
dfs(x - i, y - i, x, y);
}
if (x - i >= 1 && y + i <= n && vis[x - i][y + i]) {
dfs(x - i, y + i, x, y);
}
if (x + i <= n && y - i >= 1 && vis[x + i][y - i]) {
dfs(x + i, y - i, x, y);
}
if (x + i <= n && y + i <= n && vis[x + i][y + i]) {
dfs(x + i, y + i, x, y);
}
}
}
if (ty == 'N') {
if (x >= 3 && y >= 2 && vis[x - 2][y - 1]) {
dfs(x - 2, y - 1, x, y);
}
if (x >= 3 && y < n && vis[x - 2][y + 1]) {
dfs(x - 2, y + 1, x, y);
}
if (x >= 2 && y >= 3 && vis[x - 1][y - 2]) {
dfs(x - 1, y - 2, x, y);
}
if (x >= 2 && y < n - 1 && vis[x - 1][y + 2]) {
dfs(x - 1, y + 2, x, y);
}
if (x < n && y >= 3 && vis[x + 1][y - 2]) {
dfs(x + 1, y - 2, x, y);
}
if (x < n && y < n - 1 && vis[x + 1][y + 2]) {
dfs(x + 1, y + 2, x, y);
}
if (x < n - 1 && y >= 2 && vis[x + 2][y - 1]) {
dfs(x + 2, y - 1, x, y);
}
if (x < n - 1 && y < n && vis[x + 2][y + 1]) {
dfs(x + 2, y + 1, x, y);
}
}
if (ty == 'K') {
if (x > 1 && vis[x - 1][y]) {
dfs(x - 1, y, x, y);
}
if (x < n && vis[x + 1][y]) {
dfs(x + 1, y, x, y);
}
if (y > 1 && vis[x][y - 1]) {
dfs(x, y - 1, x, y);
}
if (y < n && vis[x][y + 1]) {
dfs(x, y + 1, x, y);
}
if (x > 1 && y > 1 && vis[x - 1][y - 1]) {
dfs(x - 1, y - 1, x, y);
}
if (x > 1 && y < n && vis[x - 1][y + 1]) {
dfs(x - 1, y + 1, x, y);
}
if (x < n && y > 1 && vis[x + 1][y - 1]) {
dfs(x + 1, y - 1, x, y);
}
if (x < n && y < n && vis[x + 1][y + 1]) {
dfs(x + 1, y + 1, x, y);
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
cin >> ty;
for (long long i = 1; i <= n; ++i) {
for (long long j = 1; j <= n; ++j) {
cin >> ch;
if (ch == ty) {
vis[i][j] = 1;
if (sx == 0 && sy == 0) {
sx = i, sy = j; // 取第一个见到的点作为最终剩下的点
}
}
}
}
// cout << sx << ' ' << sy << '\n';
dfs(sx, sy, 0, 0);
for (long long i = 1; i <= n; ++i) {
for (long long j = 1; j <= n; ++j) {
if (vis[i][j]) {
cout << "NO\n";
return 0;
}
}
}
cout << "YES\n";
for (long long k = cnt; k > 1; --k) {
cout << ans[k].sx << ' ' << ans[k].sy << ' ' << ans[k].tx << ' ' << ans[k].ty << '\n';
}
return 0;
}
/*出题人设置五种移动方式的意义是什么,考验选手码力吗?我都不敢想象写错一个方式调试该有多折磨。一道绿题整出将近200行是何意味,不知道的以为我写平衡树呢。*/