题意:有个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;
}