问题描述:有一个N*M(N,M<=50)的棋盘,棋盘的每一格是三种类型之一:空地、草地、墙。机器人只能放在空地上。在同一行或同一列的两个机器人,若它们之间没有墙,则它们可以互相攻击。问给定的棋盘,最多可以放置多少个机器人,使它们不能互相攻击。
思路:跟ZOJ1002 Fire net 很相似的一道题。刚开始做这道题的时候我还以为是状压DP,后来想了下如果是状压DP的话需要考虑前面的情况太多。然后想了一下迭代加深搜索,应该也可以做。其实这道题的真正原型是求图的最大独立集问题,也就是二分图匹配问题。
因此,在问题的原型中,草地,墙这些信息不是我们所关心的,我们关心的只是空地和空地之间的联系。因此,我们很自然想到了下面这种简单的模型: 以空地为顶点,有冲突的空地间连边。
我们将每一行,每一列被墙隔开,且包含空地的连续区域称作“块”。显然,在一个块之中,最多只能放一个机器人。我们把这些块编上号。同样的,竖直方向也这样编号。
把每个横向块看作X部的点,竖向块看作Y部的点,若两个块有公共的空地,则在它们之间连边。这样,原图转化为一个二分图。
由于每条边表示一个空地,有冲突的空地之间必有公共顶点,所以问题转化为二部图的最大匹配问题。
#include<cstdio>
#include<cstring>
#define MAXN 55
#define MAXM 2550
using namespace std;
struct T
{
int v;
int next;
}edge[101000];
int cnt;
int head[MAXM];
void add_edge(int u,int v)
{
edge[cnt].v = v;
edge[cnt].next = head[u];
head[u] = cnt++;
}
int n,m;
char map[MAXN][MAXN];
int cntx,cnty;
int px[MAXN][MAXN],py[MAXN][MAXN];
int cx[MAXM],cy[MAXM];
bool vis[MAXM];
void init()
{
for(int i = 1; i <= n; i++)//联通块编号
{
int k = 1;
while(k <= m)
{
if(map[i][k] == 'o')
{
cntx++;
while(k <= m&&map[i][k] != '#')
{
px[i][k] = cntx;
k++;
}
}
else k++;
}
}
for(int j = 1; j <= m; j++)
{
int k = 1;
while(k <= n)
{
if(map[k][j] == 'o')
{
cnty++;
while(k <= n&&map[k][j] != '#')
{
py[k][j] = cnty;
k++;
}
}
else k++;
}
}
for(int i = 1; i <= n; i++)//连边
for(int j = 1; j <= m; j++)
if(map[i][j] == 'o')
add_edge(px[i][j],py[i][j]+cntx);
}
bool dfs(int u)
{
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if(!vis[v])
{
vis[v] = 1;
if(cy[v] == -1||dfs(cy[v]))
{
cy[v] = u;
cx[u] = v;
return true;
}
}
}
return false;
}
int maxmatch()
{
int res = 0;
memset(cx,-1,sizeof cx);
memset(cy,-1,sizeof cy);
for(int i = 1; i <= cntx; i++)
{
if(cx[i] == -1)
{
memset(vis,0,sizeof vis);
if(dfs(i)) res++;
}
}
return res;
}
int main()
{
int T;
scanf("%d",&T);
for(int t = 1; t <= T; t++)
{
memset(head,-1,sizeof head);
memset(px,0,sizeof px);
memset(py,0,sizeof py);
memset(map,0,sizeof map);
cntx = cnty = 0;
cnt = 0;
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++)
scanf("%s",map[i]+1);
init();
int ans = maxmatch();
printf("Case :%d\n",t);
printf("%d\n",ans);
}
}