1. 程式人生 > 實用技巧 >AT2043 [AGC004C] AND Grid

AT2043 [AGC004C] AND Grid

首先可以發現一個很簡單的想法,因為最外層是一定不會有 \(\#\) 的,所以可以考慮讓第一個網格圖將每個連通塊的最外層包起來,第二個網格圖將就選擇這個包內部的所有點即可。

但你發現這個想法是很難實現的,只能去尋找其他的做法了。

繼續沿用剛剛將連通塊貼著的想法,只不過我們現在都用一條橫線貼著連通塊。

為了保證聯通,我們讓兩個網格圖各自佔據第一列和最後一列的所有點,然後將橫線連到第一列和最後一列上。

但是這樣還是有問題,當兩個連通塊上下交錯一個距離時,兩個網格圖還是會相交,多個連通塊形成這樣的結構時,也不能通過選擇上下底面貼著聯通的辦法。

但是上面這個網格圖的結構給予了我們提示,能否用這種橫線將這一行上的連通塊串起來呢?

於此同時,為了避免橫線相交的情況,最簡單的方法就是讓一行只有一種橫線。

因為兩邊是同樣重要的,因此需要讓兩邊佔的行數儘可能相同。

這樣一來,當一個連通塊佔據兩行時,只需要讓這幾行中出現不同種類的橫線即可。

又因為兩種橫線數量相同,可以考慮直接按照奇偶分佈直線。

那麼就只需要考慮單佔一行的連通塊了。

通過上面的構造可以發現,顯然會有一遍能直接將這個聯通塊串起來,而這個連通塊的邊上一定會是另一種橫線,因為 \(i, i + 1\) 不同奇偶。

因此,本題的構造方法已經浮現出來了:

  • 令兩個網格圖分別為 \(A, B\),將第一列分配給 \(A\),最後一列分配給 \(B\),其中對於任意一行 \(i = 2k + 1\)
    \((i, 2) \sim (i, m - 1)\) 分配給 \(A\),對於 \(i = 2k\)\((i, 2) \sim (i, m - 1)\) 分配給 \(B\)

時間複雜度 \(O(nm)\)

#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = l; i <= r; ++i)
const int N = 500 + 5;
int n, m;
char s[N][N], a[N][N], b[N][N];
int read() {
    char c; int x = 0, f = 1;
    c = getchar();
    while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int main() {
    n = read(), m = read();
    rep(i, 1, n) rep(j, 1, m) a[i][j] = b[i][j] = '.';
    rep(i, 1, n) {
        scanf("%s", s[i] + 1);
        rep(j, 1, m) if(s[i][j] == '#') a[i][j] = b[i][j] = '#';
    }
    rep(i, 1, n) a[i][1] = '#', b[i][m] = '#';
    rep(i, 1, n) {
        if(i & 1) rep(j, 1, m - 1) a[i][j] = '#';
        else rep(j, 2, m) b[i][j] = '#';
    }
    rep(i, 1, n) {
        rep(j, 1, m) printf("%c", a[i][j]);
        puts("");
    }
    puts("");
    rep(i, 1, n) {
        rep(j, 1, m) printf("%c", b[i][j]);
        puts("");
    }
    return 0;
}

可以發現,本題的構造從難以實現的想法出發,一步步簡化到達了一個非常簡單並且優秀的做法。

因此,當構造時出現實現困難的問題時,儘量簡化流程方法本質不變的情況下往往能找到非常簡單的構造方法。