1. 程式人生 > 其它 >NFLSOJ 13091 - 棋盤(貪心+圖論)

NFLSOJ 13091 - 棋盤(貪心+圖論)

題面傳送門

題意:

  • 有一個 \(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\)

的方向改為 \(c_1\) 的方向,\(c_3\) 的方向改為 \(c_2\) 的方向,……,\(c_1\) 的方向改為 \(c_k\) 的方向。不難發現調整之後環消失了,並且 LRUD 個數的限制依然成立,因為假設 \(c_1\) 填的字元是 U,既然 \(c_1\) 有指向 \(c_2\) 的邊,\(c_1,c_2\) 必然在同一列,把 \(c_1\)U 挪到 \(c_2\) 不改變這一列 U 的個數。對於其他字元也算同理。

使用 DFS 找環即可,具體實現略微有點困難,實現思路可見程式碼。考慮計算時間複雜度,定義一個時刻的勢能為圖中的邊數,那麼不難發現每消掉一個環,勢能都在減小,並且減小量就是找環過程中所遍歷的邊數,因此 DFS 的複雜度就是 \(O(|E|)\)

的,即 \(O(n^3)\)

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;
}