NFLSOJ 13091 - 棋盤(貪心+圖論)
阿新 • • 發佈:2022-06-01
題意:
- 有一個 \(n\times n\) 的網格,有 \(n^2\) 個棋子要進入這個網格。
- 每個棋子可以選擇從上、下、左、右四個方向進入網格,具體來說你先指定一個方向,再指定一行(或一列),那麼棋子會填入這一行(或列)在這個方向上第一個空位,如果這一行(或列)已經填滿則這個填棋子的方案是不合法的。
- 現在給定 \(L_i\) 表示從第 \(i\) 行的左邊進入網格的棋子個數,\(R_i,U_i,D_i\) 同理。
- 要求構造出合法的填棋子的方案,以
L(RUD)x
的格式輸出,其中 \(x\) 表示行(列)的編號。- \(n\le 300\)。
首先貪心地給每個點分配 UDLR
LR
個數等於輸入的值,UD
也同理。這一部分可以貪心,具體來說從上到下考慮所有行,將每一行中非 LR
的值優先在 \(U_i+D_i\) 較大的列中填,填完了非 LR
的值後再向剩餘的格子中填 LR
(當然也可以網路流,不過時間複雜度 \(n^{3.5}\) 可能會輕微卡常)
接下來我們考慮從每個點向其指向的點連邊,如標有 U
的點就向其上面的點連邊,表示這個點要在其上面所有點之後進入網格。如果圖是一個 DAG 那麼直接拓撲排序後取圖拓撲序逆序即可。
否則我們需要消環。假設找到一個環 \(c_1, c_2, c_3, ..., c_k\)。這時我們執行如下的調整策略:將 \(c_2\)
U
,既然 \(c_1\) 有指向 \(c_2\) 的邊,\(c_1,c_2\) 必然在同一列,把 \(c_1\) 的 U
挪到 \(c_2\) 不改變這一列 U
的個數。對於其他字元也算同理。
使用 DFS 找環即可,具體實現略微有點困難,實現思路可見程式碼。考慮計算時間複雜度,定義一個時刻的勢能為圖中的邊數,那麼不難發現每消掉一個環,勢能都在減小,並且減小量就是找環過程中所遍歷的邊數,因此 DFS 的複雜度就是 \(O(|E|)\)
const int MAXN = 500;
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};
void _NO() {puts("NO"); exit(0);}
int n, U[MAXN + 5], D[MAXN + 5], L[MAXN + 5], R[MAXN + 5], ord[MAXN + 5];
bool ingrid(int x, int y) {return 1 <= x && x <= n && 1 <= y && y <= n;}
int typ[MAXN + 5][MAXN + 5];
bool used[MAXN + 5][MAXN + 5], in_stk[MAXN + 5][MAXN + 5];
bool dfs(int x, int y) {
if (used[x][y]) return 0;
if (in_stk[x][y]) return in_stk[x][y] = 0, 1;
int d = typ[x][y];
in_stk[x][y] = 1;
for (int nx = x + dx[d], ny = y + dy[d]; ingrid(nx, ny); nx += dx[d], ny += dy[d]) {
if (dfs(nx, ny)) {
typ[nx][ny] = d;
if (in_stk[x][y]) return in_stk[x][y] = 0, 1;
else return dfs(x, y);
}
}
used[x][y] = 1; in_stk[x][y] = 0;
int tmp = (typ[x][y] < 2) ? y : x;
printf("%c%d\n", "UDLR"[typ[x][y]], tmp);
return 0;
}
int main() {
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &U[i]);
for (int i = 1; i <= n; i++) scanf("%d", &D[i]);
for (int i = 1; i <= n; i++) scanf("%d", &L[i]);
for (int i = 1; i <= n; i++) scanf("%d", &R[i]);
for (int i = 1; i <= n; i++) if (U[i] + D[i] > n || L[i] + R[i] > n) _NO();
memset(typ, -1, sizeof(typ));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) ord[j] = j;
sort(ord + 1, ord + n + 1, [&](int x, int y) {return U[x] + D[x] > U[y] + D[y];});
for (int j = 1; j <= n - L[i] - R[i]; j++) {
int cur = ord[j];
if (!U[cur] && !D[cur]) _NO();
if (U[cur]) U[cur]--, typ[i][cur] = 0;
else D[cur]--, typ[i][cur] = 1;
}
for (int j = 1; j <= n; j++) if (!~typ[i][j]) {
if (L[i]) L[i]--, typ[i][j] = 2;
else R[i]--, typ[i][j] = 3;
}
}
// for (int i = 1; i <= n; i++) {
// for (int j = 1; j <= n; j++)
// putchar("UDLR"[typ[i][j]]);
// printf("\n");
// }
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++)
if (!used[i][j]) dfs(i, j);
return 0;
}