1 /************************************************************************* 2 > File Name: hdu-4185.oil_skimming.cpp 3 > Author: CruelKing 4 > Mail: 2016586625@qq.com 5 > Created Time: 2019年09月03日 星期二 09时12分12秒 6 本题思路:简单分析过后就可以知道如果一点a被另一个点b匹配,那么和b匹配的点c不可能和点a匹配,因为每个结点都只能匹配四个方向,所以这个匹配的图就可以看做是一个二分图,因此对于每个结点,将能和他匹配的边存入,接着跑一波二分匹配之后我们得到的是无向图情况下的最大匹配,所以除以二就是最后的答案. 7 ************************************************************************/ 8 9 #include <cstdio> 10 #include <cstring> 11 #include <cmath> 12 using namespace std; 13 14 const int maxn = 600 + 5; 15 char str[maxn][maxn]; 16 17 int n; 18 int linker[maxn * maxn]; 19 bool used[maxn * maxn]; 20 int tot, head[maxn * maxn]; 21 int un[maxn * maxn], cnt; 22 23 struct Edge { 24 int to, next; 25 } edge[maxn * maxn * 4 + 5]; 26 27 void init() { 28 memset(head, -1, sizeof head); 29 tot = 0; 30 } 31 32 void addedge(int u, int v) { 33 edge[tot] = (Edge) {v, head[u]}; 34 head[u] = tot ++; 35 } 36 37 bool dfs(int u) { 38 for(int k = head[u]; ~k; k = edge[k].next) { 39 int v = edge[k].to; 40 if(!used[v]) { 41 used[v] = true; 42 if(linker[v] == -1 || dfs(linker[v])) { 43 linker[v] = u; 44 return true; 45 } 46 } 47 } 48 return false; 49 } 50 51 int main() { 52 int k, Case = 0; 53 scanf("%d", &k); 54 while(k --) { 55 init(); 56 cnt = 0; 57 scanf("%d", &n); 58 for(int i = 0; i < n; i ++) { 59 scanf("%s", str[i]); 60 } 61 for(int i = 0; i < n; i ++) { 62 for(int j = 0; j < n; j ++) { 63 if(str[i][j] == '#') { 64 un[cnt ++] = (i * n + j + 1); 65 for(int dx = -1; dx <= 1; dx ++) { 66 for(int dy = -1; dy <= 1; dy ++) { 67 if(abs(dx - dy) == 1) { 68 if(dx + i >= 0 && dx + i < n && dy + j >= 0 && dy + j < n) { 69 if(str[dx + i][dy + j] == '#') addedge(i * n + j + 1, (i + dx) * n + j + dy + 1); 70 } 71 } 72 } 73 } 74 } 75 } 76 } 77 int res = 0; 78 memset(linker, -1, sizeof linker); 79 for(int i = 0; i < cnt; i ++) { 80 memset(used, false, sizeof used); 81 if(dfs(un[i])) res ++; 82 } 83 printf("Case %d: %d ", ++Case, res / 2); 84 } 85 return 0;
}