ZOJ1654 Place the Robots (二分图匹配)

问题描述:有一个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);
	}
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值