含有数字的块最多只有10个,暴力枚举这些块的状态,剩下的非数字单元用二分图匹配来求数量。下面拿全是 '.' 的情况来说怎么个二分图匹配求解。首先一个单位能相邻的只有周围的4个点,那么将 x 坐标+ y 坐标的奇偶性作为分组条件就可以分成一个二分图,想象一下这么分组的话就是一个黑白交叉的棋盘似的。然后相邻的格子肯定就连边,然后跑二分图匹配求最大匹配数。用总的单元格数减去这个最大匹配数,得到的就是需要求的最大独立集大小。思想就是取全部然后去掉一些点使得没有两个点相邻。(注意数字块相邻的哪些单元格不要考虑在内,就是既不算入单元格总数也不算入二分图匹配中。)
#include <iostream> #include <cstdio> #include <cstring> #include <stack> #include <queue> #include <vector> #include <map> #include <set> #include <list> #include <algorithm> #include <cmath> #include <sstream> #include <iomanip> #define endl '\n' #define fin freopen("in.txt", "r", stdin) #define fout freopen("out.txt", "w", stdout) using namespace std; typedef long long LL; typedef pair<int, int> pii; typedef pair<LL, LL> pll; typedef pair<LL, int> pli; typedef pair<int, double> pid; typedef pair<double, int> pdi; const int INF = 0x3f3f3f3f; const LL LL_INF = 0x3f3f3f3f3f3f3f3f; inline int read(){ register int x; register char c(getchar()); register bool k; while((c < '0' || c > '9') && c ^ '-') if((c = getchar()) == EOF) exit(0); if(c ^ '-') x = c & 15, k = 1; else x = 0, k = 0; while(c = getchar(), c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15); return k ? x : -x; } void write(register int a){ if(a < 0) putchar('-'), a = -a; if(a <= 9) putchar(a | '0'); else write(a / 10), putchar((a % 10) | '0'); } const int dr[] = {0, 1, 0, -1}; const int dc[] = {1, 0, -1, 0}; int N, M; char table[15][15]; int anthor_name[15]; int anci_cnt; int res; vector<int> tmp_v; set<pii> tmp_s; bool flag[15][15]; bool val(int r, int c){ return r >= 1 && r <= N && c >= 1 && c <= M; } bool connect(){ for(int i = 0; i < tmp_v.size(); i++){ for(int j = i + 1; j < tmp_v.size(); j++){ int x = min(tmp_v[i], tmp_v[j]), y = max(tmp_v[i], tmp_v[j]); if(tmp_s.count(pii(x, y))) return true; } } return false; } const int MAX_V = 100 + 5; // 复杂度为|V|*|E| vector<int> G[MAX_V]; // 图的邻接表表示 int match[MAX_V]; // 所匹配的顶点 bool used[MAX_V]; // DFS中用到的访问标记 void add_edge(int u, int v){ G[u].push_back(v); G[v].push_back(u); } bool dfs(int v){ used[v] = true; for(int i = 0; i < G[v].size(); i++){ int u = G[v][i], w = match[u]; if(w < 0 || !used[w] && dfs(w)){ match[v] = u; match[u] = v; return true; } } return false; } // 求[0, V - 1]顶点编号的二分图的最大匹配 int bipartite_matching(int V){ int res = 0; memset(match, -1, V * sizeof(int)); for(int v = 0; v < V; v++){ if(match[v] < 0){ memset(used, 0, V * sizeof(bool)); if(dfs(v)) res++; } } return res; } int main(){ int t = read(); for(int tt = 1; tt <= t; tt++){ N = read(), M = read(); anci_cnt = 0; res = 0; for(int i = 0; i < 15; i++) anthor_name[i] = -1; for(int i = 1; i <= N; i++){ for(int j = 1; j <= M; j++){ char ch = getchar(); if(ch != '.'){ if(anthor_name[ch - '0'] == -1){ anthor_name[ch - '0'] = anci_cnt++; ch = '0' + anci_cnt - 1; }else{ ch = '0' + anthor_name[ch - '0']; } } table[i][j] = ch; } getchar(); } /*for(int i = 1; i <= N; i++){ for(int j = 1; j <= M; j++){ cout << table[i][j]; } cout << endl; }*/ // 相邻预处理 tmp_s.clear(); for(int i = 1; i <= N; i++){ for(int j = 1; j <= M; j++){ if(table[i][j] == '.') continue; int tmp1 = table[i][j] - '0', tmp2; for(int k = 0; k < 4; k++){ int ti = i + dr[k], tj = j + dc[k]; if(val(ti, tj)){ if(table[ti][tj] != '.'){ tmp2 = table[ti][tj] - '0'; if(tmp1 != tmp2) tmp_s.insert(pii(min(tmp1, tmp2), max(tmp1, tmp2))); } } } } } /*for(auto it:tmp_s){ cout << it.first << ' ' << it.second << endl; }*/ //枚举 int enum_num = 1 << anci_cnt; for(int i = 0; i < enum_num; i++){ tmp_v.clear(); memset(flag, 0, sizeof(flag)); int tot = 0; for(int j = 0; j < anci_cnt; j++){ //cout << 1 << endl; if(i >> j & 1){ tmp_v.push_back(j); tot++; } } if(connect()) continue; //cout << tot << endl; for(int i = 0; i < tmp_v.size(); i++){ for(int r = 1; r <= N; r++){ for(int c = 1; c <= M; c++){ if(table[r][c] == '0' + tmp_v[i]){ for(int k = 0; k < 4; k++){ int tr = r + dr[k], tc = c + dc[k]; if(val(tr, tc)) flag[tr][tc] = 1; } } } } } for(int r = 1; r <= N; r++){ for(int c = 1; c <= M; c++){ if(table[r][c] != '.') flag[r][c] = 1; } } /*cout << endl; for(int i = 1; i <= N; i++){ for(int j = 1; j <= M; j++){ cout<< flag[i][j]; } cout << endl; }*/ for(int i = 0; i < MAX_V; i++) G[i].clear(); for(int r = 1; r <= N; r++){ for(int c = 1; c <= M; c++){ if(flag[r][c] == 1) continue; tot++; if((r + c) % 2) continue; for(int k = 0; k < 4; k++){ int tr = r + dr[k], tc = c + dc[k]; if(val(tr, tc) && flag[tr][tc] == 0){ add_edge((tr - 1) * M + tc - 1, (r - 1) * M + c - 1); } } } } int tmp = bipartite_matching(N * M); //cout << tmp << endl; tot -= tmp; res = max(res, tot); } printf ("Case #%d: %d\n", tt, res); } return 0; }