题意:给一个n * n的棋盘,有若干个车,现在要给车涂色使得同一行或者同一列的任意两个车颜色不同,问至少需要多少种颜色,并给出其中一种涂色方式。
思路:每一行和每一列的车的个数的最大值就是需要多少种颜色。分两个集合,行集合和列集合,然后在有车的位置(i, j)让i属于行集合,j属于列集合,并连一条边,因为要求出涂色方式,所以需要给这个二分图补边,使得所有点的入度和出度均为所需颜色数,每次求一次二分图匹配,在是车的位置填色,不是车的位置忽略。
#include<cstdio>
#include<cstring>
#include<vector>
#include<set>
#include<cstdlib>
#include<algorithm>
const int maxn = 2 * 1e2 + 10;
using namespace std;
int n, T;
int c[maxn], r[maxn];
int col[maxn][maxn];
int use[maxn], from[maxn];
char s[maxn][maxn];
vector<int> G[maxn];
void init() {
memset(c, 0, sizeof c);
memset(r, 0, sizeof r);
memset(col, 0, sizeof col);
for(int i = 0; i < maxn; i++)
G[i].clear();
}
int dfs(int x) {
for(int i = 0; i < G[x].size(); i++) {
int u = G[x][i];
if(use[u]) continue; use[u] = 1;
if(from[u] == -1 || dfs(from[u])) {
from[u] = x; return 1;
}
}
return 0;
}
int maxp() {
int sum = 0;
memset(from, -1, sizeof(from));
for(int i = 0; i < n; i++) {
memset(use, 0, sizeof(use));
sum += dfs(i);
}
return sum;
}
int main() {
scanf("%d", &T);
while(T--) {
init();
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%s", s[i]);
}
int num = 1, ans = 0;
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
if(s[i][j] == '*') {
c[i]++; r[j]++;
G[i].push_back(j);
}
}
}
for(int i = 0; i < n; i++)
ans = max(ans, max(c[i], r[i]));
for(int i = 0; i < n; i++) {
if(G[i].size() == ans) continue;
for(int j = 0; j < n && G[i].size() < ans; j++) {
while(r[j] < ans && G[i].size() < ans) {
r[j]++; G[i].push_back(j);
}
}
}
while(1) {
if(!maxp()) break;
for(int i = 0; i < n; i++) {
if(from[i] == -1) continue;
if(s[from[i]][i] == '*') col[from[i]][i] = num;
for(int j = 0; j < G[from[i]].size(); j++) {
if(G[from[i]][j] != i) continue;
G[from[i]].erase(G[from[i]].begin() + j);
break;
}
}
num++;
}
printf("%d\n", ans);
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
printf("%d", col[i][j]);
printf("%c", j < n - 1 ? ' ' : '\n');
}
}
}
return 0;
}