jiejiejiang2004

题解:2024牛客多校赛第二场 A Floor Tiles(思维)

2024 Nowcoder Multi-University Training Contest 2

Problem A. Floor Tiles

题目大意

给你两种正方形图案,分别为以下两种:

1

再给你三个整数 \(N,M,K\) ,表示你需要用这两种图案,拼成一个 \(N\)\(M\) 行的矩形。
由于这两种图案十分特殊,他们能无缝衔接在一起。因此你需要让这个矩形内含有 \(K\) 条不相连的曲线。
比如:图一内有 \(4\) 条曲线,图二内有 \(5\) 条曲线

curve

题目还会给你两个整数 \(x、y\) 和一个字母 \(t\) ,表示在这个矩形内的的第 \(x\) 列第 \(y\) 行必须是输入的字母 \(t\)
如果这种情况可以成立,那么输出 YES 并在下一行开始输出符合上述规则的图形之一;
如果这种情况不能成立,那么输出 NO

我们小组的思路

首先,我们发现:

  • 一个 \(N\)\(M\) 行的矩形内,如果曲线条数最少,那么肯定有一种情况是全部图案统一(要么全部为 \(A\) ,要么全部为 \(B\) ),且这个数量为 \(N + M\)
  • 在上条发现的基础上,增添一种 \(2 * 2\) 的结构可以形成一个圆形,同时增加一条曲线:
AB
BA
  • 再在上一条发现的基础上,一个 \(N * M\) 的矩形内最多有 \(( N - 1 ) * ( M - 1 ) / 2\) 个圆。特殊的,如果边长均为偶数同时左上角的图案必须为 A 的话 还能再多一个 (虽然后面我们发现并没有什么用但是课可以帮助我们能理解这种图形)
  • 如果某个地方的图案不能(或者说不需要)改变的话——这种情况就是第二条中的结构中:假设全部为 \(A\) 的情况下增加 \(B\) ,那么 \(A\) 就是不需要改变的(反之假设全部为 \(B\) 的情况下增加 \(A\) ,那么 \(B\) 就是不需要改变的),那么这个图案沿着45°斜线去移动能到达的所有位置都不会改变
举个例子
ABABABAB
BABABABA
ABABABAB
BABABABA
这个矩形里所有的A都是不改变的(因为一旦变了,曲线数量就会减少)
同样的,B也是如此

其次,我们设计程序

  1. 先输入 \(n , m , k , x , y , t\),然后计算出 min(曲线最少情况)
  2. 第一,判断只有一行或者一列的时候能不能行,因为这种情况下是不能形成圆形区域的,所以曲线数量一定为 \(N + M\) 。(特判1)
  3. 第二,判断 \(k\) 是否小于最小值。(特判2)
  4. 沿着斜线,把不动点平移到左上角,使他到达 \(( 0 , 1 )\) 或者 \(( 1 , 0 )\) ,这样就能确定哪些位置是需要改变的了(因为要改变才能增加曲线数量)、
  5. 初始化字符串矩阵(假设全部都是不动点的那个字母)和曲线数量 \(( N + M )\)
  6. 每一行去遍历寻找哪些点是可以改变的(详情参考上面的例子),在改变的同时检测是否出现了能形成圆形的结构,更新曲线数量
  7. 当曲线数量到达目标数量时,可以输出 YES 和字符串矩阵;
  8. 如果整个矩阵遍历完,还达不到目标数量的话,就输出 NO

我们的代码

#include <bits/stdc++.h>
#define int long long

const int N = 805;
int T;
int n,m,k,x,y;
std::string s_t;
char t,c_t;
int min,max,add,sum;
std::string str[N];

void solve() {
    std::cin >> n >> m >> k;
    min = n + m;

    std::cin >> x >> y >> s_t;
    t = s_t[0];
    c_t = (t == 'A' ? 'B' : 'A');

    if (n == 1 || m == 1) {
        if (k != n + m) std::cout << "NO\n";
        else {
            std::cout << "YES\n";
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= m; j++) {
                    std::cout << t;
                }
                std::cout << "\n";
            }
        }
        return;
    }   //特判边长为1

    if (min > k) {
        std::cout << "NO\n";
        return;
    }   //判断是否落在范围之内

    int umi = std::min(x, y);
    x -= umi;
    y -= umi;
    x &= 1;
    y &= 1;
    if (x > y) std::swap(x, y);
    //等效转移不动点

    int u = 1 - y;
    //确定哪个位置加A或者B

    add = k - min;
    for (int i = 0; i < n; i++) {
        str[i].resize(m);
        for (int j = 0; j < m; j++) {
            str[i][j] = t;
        }
    }
    //初始化,假设全部为t时,曲线数量最少
    
    sum = min;
    for (int i = 0 ; i < n && sum < k; i ++) {
        for(int j = u; j < m && sum < k; j += 2) {
            str[i][j] = c_t;

            if (c_t == 'A' && i >= 1 && j >= 1 && str[i - 1][j - 1] == 'A' && str[i - 1][j] == 'B' && str[i][j - 1] == 'B') sum++;
            else if (c_t == 'B' && i >= 1 && j < m-1 && str[i - 1][j] == 'A' && str[i - 1][j + 1] == 'B' && str[i][j + 1] == 'A') sum++;
        }
        u = 1 - u;
    }

    if(sum != k) std::cout << "NO\n";
    else {
        std::cout << "YES\n";
        for (int z = 0; z < n; z++) std::cout << str[z] << "\n";
    }

}

signed main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    T = 1;
    std::cin >> T;
    while (T--) solve();
    return 0;
}

posted on 2024-07-18 22:32  Jiejiejiang  阅读(24)  评论(0编辑  收藏  举报

导航