Codeforces 1316D Nash Matrix
Description
有一种用 $n \times n$ 的网格玩的游戏。
- 当 $(i,j)$ 填的是 L 时,你会走到 $(i, j - 1)$;
- 当 $(i,j)$ 填的是 R 时,你会走到 $(i, j + 1)$;
- 当 $(i,j)$ 填的是 U 时,你会走到 $(i - 1, j )$;
- 当 $(i,j)$ 填的是 D 时,你会走到 $(i + 1, j)$;
- 当 $(i,j)$ 填的是 X 时,你将不动。
当你从 $(i, j)$ 出发,最后将会停止在 $(x_{(i, j)}, y_{(i, j)})$。或者,如果你永远不会停止,$(x_{(i, j)}, y_{(i, j)})$ 是 $(-1, -1)$。
现在给出所有的 $(x_{(i, j)}, y_{(i, j)})$,试构造出原网格。如果无法构造,输出 INVALID,否则输出 VALID,并输出一种合法网格。
Solution
分两个部分,第一部分处理会终止的结点,第二部分处理不会终止的结点
试想,对于一个 $(i, j)$,停止于 $(ex_{(i, j)}, ey_{(i, j)})$,那么,我们先随便拉出来一条路径,一定会知道,路径上的每一个点都会停止于 $ {(ex_{(i, j)}, ey_{(i, j)})}$,所以,这张图一定是若干个连通块,某一个连通块内,点的停止点应该是相同的。
我们找到所有的自停止点,也就是 $(i, j) = (ex_{(i, j)}, ey_{(i, j)})$,显然这个点应当是 X,然后我们从它开始,在满足停止点是它的连通块内构造答案。这很简单,只要我们 DFS 一遍,把访问到一个点的方向都反过来就行了。
void DFS(int p, int q, int sx, int sy) // 当前在 (p, q),处理所有停止点是 (sx, sy) 的 { REP(i, 0, 3) // 枚举方向,dx[], dy[] 是增量数组 { int tx = p + dx[i], ty = q + dy[i]; if(tx >= 1 && tx <= n && ty >= 1 && ty <= n && // 不越界 ex[tx][ty] == sx && ey[tx][ty] == sy && !ans[tx][ty]) // 终止点是 (sx, sy) && 没访问 { ans[tx][ty] = dc[i]; // dc[] 存的是反方向 DFS(tx, ty, sx, sy); } } }
然后是 $-1$ 的情况。如果一个 $-1$ 连通块的大小为 $1$,那么结果肯定是 INVALID,因为一个点怎么也没法做到不停止。否则,我们选择这个连通块内相邻的两个点,把它们指向对方,然后让其他 $-1$ 都走到这两个点上面来,这个连通块就合法了。
这需要再写一种 DFS 吗?答案是否定的,我们把上面 DFS 的参数 $(sx ,s y)\gets (-1, -1)$,就是自动在找 $-1$ 连通块了。
别忘了构造题的惯常套路:构造完后再检验一遍。
#include <bits/stdc++.h> #define REP(i, x, y) for(register int i = x; i <= y; i++) using namespace std; const int N = 1e3 + 5; const int dx[4] = {0, 1, 0, -1}; const int dy[4] = {1, 0, -1, 0}; const char dc[4] = {'L', 'U', 'R', 'D'}; int n, ex[N][N], ey[N][N]; char ans[N][N]; void DFS(int p, int q, int sx, int sy) { REP(i, 0, 3) { int tx = p + dx[i], ty = q + dy[i]; if(tx >= 1 && tx <= n && ty >= 1 && ty <= n && ex[tx][ty] == sx && ey[tx][ty] == sy && !ans[tx][ty]) { ans[tx][ty] = dc[i]; DFS(tx, ty, sx, sy); } } } int main() { scanf("%d", &n); REP(i, 1, n) REP(j, 1, n) scanf("%d %d", &ex[i][j], &ey[i][j]); REP(i, 1, n) REP(j, 1, n) if(ex[i][j] == i && ey[i][j] == j) ans[i][j] = 'X', DFS(i, j, i, j); REP(i, 1, n) REP(j, 1, n) { if(ex[i][j] == -1 && !ans[i][j]) { // 注意,这里两种情况即可,因为 ...-1 的情况在 i=i-1 / j=j-1 的时候枚举到了 if(ex[i + 1][j] == -1) ans[i][j] = 'D', ans[i + 1][j] = 'U', DFS(i, j, -1, -1), DFS(i + 1, j, -1, -1); if(ex[i][j + 1] == -1) ans[i][j] = 'R', ans[i][j + 1] = 'L', DFS(i, j, -1, -1), DFS(i, j + 1, -1, -1); } } bool invalid = false; REP(i, 1, n) REP(j, 1, n) invalid |= !ans[i][j]; if(invalid) return puts("INVALID") && 0; puts("VALID"); REP(i, 1, n) { REP(j, 1, n) putchar(ans[i][j]); puts(""); } return 0; }