wiki-3052-多米诺-二分图匹配

题目:一个矩形可以划分成M*N个小正方形,其中有一些小正方形不能使用。一个多米诺骨牌占用两个相邻的小正方形。试问整个区域内最多可以不重叠地放多少个多米诺骨牌且不占用任何一个被标记为无法使用的小正方形。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;

int n,m,k,size=0,g;
int can[60][60],code[60][60],num[60][60],ffff[5510];
int v[5510],first[5510],next[5010];
int vis[5510];

void init()
{
}

void readdata()
{
	memset(next,-1,sizeof(next));
	memset(first,-1,sizeof(first));
	memset(can,1,sizeof(can));
	memset(code,1,sizeof(code));
	memset(ffff,-1,sizeof(ffff));
	scanf("%d%d%d",&m,&n,&k);
	for(int i=1;i<=k;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		can[x][y]=0;
	}
}

void insert(int x,int y)
{
	size++;
	v[size]=y;
	next[size]=first[x];
	first[x]=size;
}

bool in(int x,int y)
{
	if(x>0&&x<=m&&y>0&&y<=n) return true;
	else return false;
}

void makemap()
{
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(code[i][j])
			{
				code[i-1][j]=0;
				code[i+1][j]=0;
				code[i][j-1]=0;
				code[i][j+1]=0;
			}
		}
	}
	int x=0,y=0;
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(can[i][j])
			{
				if(code[i][j])
				{
					x++;
					num[i][j]=x;
				}
			}
		}
	}
	g=x;
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(can[i][j])
			{
				if(!code[i][j])
				{
					y++;
					num[i][j]=y;
				}
			}
		}
	}
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(can[i][j])
			{
				if(code[i][j])
				{
					if(can[i-1][j]&&in(i-1,j))
					{
						insert(num[i][j],num[i-1][j]);
					}
					if(can[i+1][j]&&in(i+1,j))
					{
						insert(num[i][j],num[i+1][j]);
					}
					if(can[i][j-1]&&in(i,j-1))
					{
						insert(num[i][j],num[i][j-1]);
					}
					if(can[i][j+1]&&in(i,j+1))
					{
						insert(num[i][j],num[i][j+1]);
					}
				}
			}
		}
	}
}

int clockvis;

bool find(int k)
{
	for(int e=first[k];e!=-1;e=next[e])
	{
		int y=v[e];
		if(vis[y]==clockvis) continue;
		vis[y]=clockvis;
		if(ffff[y]==-1 || find(ffff[y]))
		{
			ffff[y]=k;
			return true;
		}
	}
	return false;
}

void work()
{
	makemap();
	int ans=0;
	for(int i=1;i<=g;i++)
	{
		clockvis=i;
		if(find(i)) ans++;
	}
    printf("%d\n",ans);
}

int main()
{
	init();
	readdata();
	work();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值