ZOJ 3780 Paint the Grid Again(隐式图拓扑排序)
Leo has a grid with N × N cells. He wants to paint each cell with a specific color (either black or white).
Leo has a magical brush which can paint any row with black color, or any column with white color. Each time he uses the brush, the previous color of cells will be covered by the new color. Since the magic of the brush is limited, each row and each column can only be painted at most once. The cells were painted in some other color (neither black nor white) initially.
Please write a program to find out the way to paint the grid.
Input
There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:
The first line contains an integer N (1 <= N <= 500). Then N lines follow. Each line contains a string with N characters. Each character is either 'X' (black) or 'O' (white) indicates the color of the cells should be painted to, after Leo finished his painting.
Output
For each test case, output "No solution" if it is impossible to find a way to paint the grid.
Otherwise, output the solution with minimum number of painting operations. Each operation is either "R#" (paint in a row) or "C#" (paint in a column), "#" is the index (1-based) of the row/column. Use exactly one space to separate each operation.
Among all possible solutions, you should choose the lexicographically smallest one. A solution X is lexicographically smaller than Y if there exists an integer k, the first k - 1 operations of X and Y are the same. The k-th operation of X is smaller than the k-th in Y. The operation in a column is always smaller than the operation in a row. If two operations have the same type, the one with smaller index of row/column is the lexicographically smaller one.
Sample Input
2 2 XX OX 2 XO OX
Sample Output
R2 C1 R1 No solution
题目链接:ZOJ 3780
比赛的时候真心没想出来,这题居然是用拓扑排序做,不得不膜拜一下。
做法:题目中最重要的条件就是每一行或每一列最多只能变换一次,考虑一个格子$(i,j)$,若它是'X'则说明它肯定是先刷$j$列后刷$i$行变换得到的;若为'O'则是先刷$i$行后刷$j$列的变换得到,那么$n*n$个格子可以得到$n*n$种顺序条件,最后再看能否存在一种拓扑序列同时符合这么多的条件,若符合则说明按照这个拓扑序列去刷可以刷出给定的字符样式。那么问题就成了拓扑排序问题了。最后题目要求字典序最小的方案,由于列变换字符'C'的字典序比行变换'R'的字典序小,因此把列号设为$1$~$n$,行号设为$n+1$~$2n$,而且要求变换的行列坐标也要最小,因此用最小堆的优先队列来代替普通队列进行拓扑排序,一开始的入度为0的点实际上是不用刷的,因为一开始的点是没有行、列顺序要求的。比如单一个点'X',拓扑序为第1列->第1行,但是显然刷第1列这个操作是多余的。
代码:
#include <stdio.h> #include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define LC(x) (x<<1) #define RC(x) ((x<<1)+1) #define MID(x,y) ((x+y)>>1) #define CLR(arr,val) memset(arr,val,sizeof(arr)) #define FAST_IO ios::sync_with_stdio(false);cin.tie(0); typedef pair<int, int> pii; typedef long long LL; const double PI = acos(-1.0); const int N = 510; struct edge { int to, nxt; edge() {} edge(int _to, int _nxt): to(_to), nxt(_nxt) {} } E[N * N]; char s[N][N]; int head[N << 1], tot; int deg[N << 1], no_use[N << 1]; vector<int>ans; void init() { CLR(head, -1); tot = 0; CLR(deg, 0); CLR(no_use, 0); ans.clear(); } inline void add(int s, int t) { E[tot] = edge(t, head[s]); head[s] = tot++; } int Top_sort(int n) { int i, u; priority_queue<int, vector<int>, greater<int> >Q; for (i = 1; i <= n; ++i) { if (!deg[i]) { Q.push(i); no_use[i] = 1; } } while (!Q.empty()) { u = Q.top(); Q.pop(); ans.push_back(u); for (i = head[u]; ~i; i = E[i].nxt) { int v = E[i].to; if (!--deg[v]) Q.push(v); } } return (int)ans.size() == n; } int main(void) { int tcase, n, i, j; scanf("%d", &tcase); while (tcase--) { init(); scanf("%d", &n); for (i = 1; i <= n; ++i) scanf("%s", s[i] + 1); for (i = 1; i <= n; ++i) //N*N { for (j = 1; j <= n; ++j) { if (s[i][j] == 'X') add(j, i + n), ++deg[i + n]; else add(i + n, j), ++deg[j]; } } if (!Top_sort(n << 1)) puts("No solution"); else { int sz = ans.size(); for (i = 0; i < sz; ++i) { int x = ans[i]; if (no_use[x]) continue; printf("%c%d%s", x <= n ? 'C' : 'R', x <= n ? x : x - n, i == sz - 1 ? "\n" : " "); } } } return 0; }