题意:
一个N*M的矩阵,其中“.”代表空地,“0-9”代表古代建筑,如果选择了一个编号的古代建筑去重新建立,那么其对应所有该编号的古建筑都要被建立起来,如果在空地上建筑,可以只选择建立当前点。问最多能够建立多少个建筑,并且建立的所有建筑中每两个建筑不相邻,即没有公共边。
思路:
很容易想到是求最大独立集,将图中所有的两两限制关系全部抽象为边即可,但会发现将边抽象出来之后是一个无向图,不能构成一个二分图,所有需要利用最大团的BK算法去求(链接),无向图的最大独立集数 == 其补图的最大团数。
链接中的两种算法,前者会超时,后者才可以过,而且贼快= =,之前做的一道最大团的题也是后者算法快,以后求极大团才用前者,最大团全用后者吧(事实表明)。
代码:
#include <bits/stdc++.h>
using namespace std;
char s[15][15];
int t, n, m, cnt;
int used[15], _num[105][105];
int match[105], vis[105];
bool mp[105][105];
int best, num[105];
bool dfs(int *adj, int total, int cnt)
{
int t[105], k;
if(total == 0)
{
if(cnt > best)
{
best = cnt;
return true;
}
return false;
}
for(int i = 0; i < total; ++i)
{
if(cnt+total-i <= best) return false;
if(cnt+num[adj[i]] <= best) return false;
k = 0;
for(int j = i+1; j < total; ++j)
if(!mp[adj[i]][adj[j]]) t[k++] = adj[j];
if(dfs(t, k, cnt+1)) return true;
}
return false;
}
void work()
{
int adj[105], k;
best = 0;
for(int i = cnt; i >= 1; --i)
{
k = 0;
for(int j = i+1; j <= cnt; ++j)
if(!mp[i][j]) adj[k++] = j;
dfs(adj, k, 1);
num[i] = best;
}
}
int main()
{
//freopen("in.txt", "r", stdin);
scanf("%d", &t);
for(int _ = 1; _ <= t; ++_)
{
memset(match, -1, sizeof match);
memset(used, 0, sizeof used);
memset(mp, 0, sizeof mp);
scanf("%d %d", &n, &m);
cnt = 0;
for(int i = 1; i <= n; ++i)
scanf("%s", s[i]+1);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
{
if(s[i][j] == '.')
{
_num[i][j] = ++cnt;
int tx = i-1, ty = j-1;
if(tx > 0)
mp[cnt][_num[tx][j]] = mp[_num[tx][j]][cnt] = 1;
if(ty > 0)
mp[cnt][_num[i][ty]] = mp[_num[i][ty]][cnt] = 1;
}
else
{
int tmp;
if(used[s[i][j]-'0'] != 0)
tmp = used[s[i][j]-'0'];
else tmp = ++cnt, used[s[i][j]-'0'] = cnt;
_num[i][j] = tmp;
int tx = i-1, ty = j-1;
if(tx > 0 && tmp != _num[tx][j])
mp[tmp][_num[tx][j]] = mp[_num[tx][j]][tmp] = 1;
if(ty > 0 && tmp != _num[i][ty])
mp[tmp][_num[i][ty]] = mp[_num[i][ty]][tmp] = 1;
}
}
work();
printf("Case #%d: %d\n", _, best);
}
return 0;
}
继续加油~