UVa 10615 - Rooks
Published on 2015-12-24描述
给出一个 的矩阵,有一些格子上放了车子,用 *
表示,你要用尽量少的颜色为他们染色,使得同一行以及同一列两辆车颜色均不相同,并输出方案。
样例输入
2 2 *. ** 4 *.*. *.*. ***. ..**
样例输出
2 2 0 1 2 4 1 0 2 0 3 0 1 0 2 1 3 0 0 0 4 1
分析
简单分析可以知道,由于每行每列的车子的颜色都不能相同,所以需要的最少颜色数 一定是车子最多的行/列的的车子数量。问题关键在于如何构造解。
我们利用二分图完美匹配解决,左侧 集为行,右侧 集为列,对于车子坐标 ,我们连接左侧标号为 与右侧标号为 的节点,于是我们把车子抽象为边,目标是给这些边染色。
可以观察到,由于同行列车子颜色不能相同,如果我们求出了一个完美匹配,那么匹配边(代表着某个车子)之间一定是没有冲突的。每一次的匹配边都可以染成相同的颜色,之后将其删去。每一次匹配所有的点度 - 1,故 次完美匹配就行了。
比较关键的一点是补边的操作,每一次最大匹配若不是完美匹配,最大匹配会造成下一次匹配不一定是最大, 次二分匹配后,可能会有边未被染色,所以要补边。细节见代码。
代码
// Created by Sengxian on 12/24/15. // Copyright (c) 2015年 Sengxian. All rights reserved. // Uva 10615 完美匹配+补边 #include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <vector> #include <stack> #include <queue> using namespace std; const int maxn = 100 + 3; bool real[maxn][maxn], s[maxn], t[maxn]; int n, inrow[maxn], incow[maxn], match[maxn], col[maxn][maxn], w[maxn][maxn]; bool dfs(int u) { s[u] = true; for(int v = 0; v < n; ++v) if(w[u][v] > 0 && !t[v]) { t[v] = true; if(match[v] == -1 || dfs(match[v])) { match[v] = u; return true; } } return false; } int bipartite() { int cnt = 0; fill(match, match + n, -1); for(int i = 0; i < n; ++i) { fill(s, s + n, false); fill(t, t + n, false); cnt += dfs(i); } return cnt; } inline void relax(int &a, int b) { if(b > a) a = b; } inline int ReadInt() { int n = 0, ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); do { n = n * 10 + ch - '0'; ch = getchar(); }while(ch >= '0' && ch <= '9'); return n; } int main() { int caseNum = ReadInt(); while(caseNum--) { n = ReadInt(); fill(inrow, inrow + n, 0); fill(incow, incow + n, 0); for(int i = 0; i < n; ++i) { for(int j = 0; j < n; ++j) { w[i][j] = real[i][j] = getchar() == '*'; if(real[i][j]) inrow[i]++, incow[j]++; } while(getchar() == ' '); } int ans = 0; for(int i = 0; i < n; ++i) relax(ans, max(inrow[i], incow[i])); for(int i = 0; i < n; ++i) if(inrow[i] < ans) for(int j = 0; j < n; ++j) { //需用while,防止补不满,能补则补 while(inrow[i] < ans && incow[j] < ans) { inrow[i]++; incow[j]++; w[i][j]++; //w[i][j] 表示 i -> j 一共有几条边 } } for(int i = 1; i <= ans; ++i) { bipartite(); for(int j = 0; j < n; ++j) { if(match[j] == -1) continue; if(real[match[j]][j]) col[match[j]][j] = i; w[match[j]][j]--; } } printf("%d\n", ans); for(int i = 0; i < n; ++i) for(int j = 0; j < n; ++j) printf("%d%c", real[i][j] ? col[i][j] : 0, j + 1 == n ? '\n' : ' '); } return 0; }