zoukankan      html  css  js  c++  java
  • Codeforces 1316D Nash Matrix

    Description

    有一种用 $n imes 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;
    }
  • 相关阅读:
    ASP.NET Core 2.1 Web API + Identity Server 4 + Angular 6 + Angular Material 实战小项目视频
    .NET Core TDD 前传: 编写易于测试的代码 -- 单一职责
    学习Identity Server 4的预备知识 (误删, 重补)
    .NET Core TDD 前传: 编写易于测试的代码 -- 全局状态
    .NET Core TDD 前传: 编写易于测试的代码 -- 依赖项
    .NET Core TDD 前传: 编写易于测试的代码 -- 构建对象
    .NET Core TDD 前传: 编写易于测试的代码 -- 缝
    SpringBoot入门教程(十)应用监控Actuator
    SpringBoot入门教程(九)定时任务Schedule
    SpringBoot入门教程(八)配置logback日志
  • 原文地址:https://www.cnblogs.com/syksykCCC/p/CF1316D.html
Copyright © 2011-2022 走看看