暑假-二分图-D - Uncle Tom's Inherited Land

题意:有个n*m的地,有k个位置有水塘,现在想把这些地卖了【卖的地必须是1*2的长方形,并且长方形不含水塘】

问最多能卖多少块地。

思路:二分图的最大边独立集,等于最大匹配。

对于一个位置mapg[i][j]如果此位置不是水塘,则判断此位置上下左右四个方向的位置是否为水塘,若不是水塘则

说明可以与此位置的空地组成一块能卖的长方形【即此位置也上下左右不是水塘的位置连一条边】。最后走一遍

二分图最大匹配模板就可。

注意:由于边的两个点是相互的, 即1能和2组成长方形,那么2也能和1组成长方形,所以最终求得的结果

为正真结果的两倍。


/*
628KB
0MS
*/
#include<iostream>
#include<cstring>
#include<vector>
#include<stdio.h>
using namespace std;
const int MAXN = 105;
const int MAXP = 10005;//n,m最大为100,如果没有水塘那么点最多是100*100
#define INF 65535//一个不可能达到的值,标记为水塘
int mapg[MAXN][MAXN];//初始图
int link[MAXP];//点i也二分图另一半的哪个顶点匹配
int vis[MAXP];//判断是否访问过
int dist[4][2] = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } };
vector <int> map[MAXP];
//邻接表存储二分图【主图】
int n, m, k;
bool find(int x)
{
	for (int i = 0; i < map[x].size(); i++)//考虑所以和x有关系的点
	{
		int u = map[x][i];
		if (!vis[u])//x与u邻接,且没有访问过
		{
			vis[u] = 1;//标记访问
			if (link[u] == -1 || find(link[u]))
			{//如果u没有匹配,或者u已经匹配了,但从link[u]出发可以找到增广路
				link[u] = x;//把x匹配给u
				return true;
			}
		}
	}
	return false;
}
void init()//初始化
{
	memset(link, -1, sizeof(link));
	memset(mapg, 0, sizeof(mapg));
}
int main()
{
	while (scanf("%d%d",&n,&m)!=EOF)
	{
		if (n == 0 && m == 0)
		{
			break;
		}
		init();//初始化
		int u, v, ans = 0, p = 0;
		scanf("%d", &k);
		while (k--)
		{
			scanf("%d%d", &u, &v);
			mapg[u][v] = INF;//水塘位置标记
		}
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= m; j++)
			{
				if (mapg[i][j]!=INF)//把不是水塘的位置给一个编码
				{
					mapg[i][j] = p++;//从0到((n*m)-k-1)
				}
			}
		}
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= m; j++)
			{
				if (mapg[i][j] != INF)
				{
					for (int v = 0; v < 4; v++)//上下左右四个方向。
					{
						int x = i + dist[v][0];
						int y = j + dist[v][1];
						if ((x > 0 && x <= n) && (y > 0 && y <= m) && mapg[x][y] != INF)
						{//不越界并且该位置不是水塘
							map[mapg[i][j]].push_back(mapg[x][y]);//则连一条边
						}
					}
				}
			}
		}
		for (int i = 0; i < p ; i++)
		{
			memset(vis, 0, sizeof(vis));
			if (find(i)) //从每个未盖点出发进行寻找增广路
			{//每找到一条增广路,可使得匹配数加1
				ans++;
			}
		}
		printf("%d\n",(ans/2));
		for (int i = 0; i < p; i++)//清除数组
		{
			map[i].clear();
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值