2015
09-17

# HDU 4619-Warm up 2-图-[解题报告]HOJ

Problem Description
Some 1×2 dominoes are placed on a plane. Each dominoe is placed either horizontally or vertically. It’s guaranteed the dominoes in the same direction are not overlapped, but horizontal and vertical dominoes
may overlap with each other. You task is to remove some dominoes, so that the remaining dominoes do not overlap with each other. Now, tell me the maximum number of dominoes left on the board.

Input
There are multiple input cases.
The first line of each case are 2 integers: n(1 <= n <= 1000), m(1 <= m <= 1000), indicating the number of horizontal and vertical dominoes.
Then n lines follow, each line contains 2 integers x (0 <= x <= 100) and y (0 <= y <= 100), indicating the position of a horizontal dominoe. The dominoe occupies the grids of (x, y) and (x + 1, y).
Then m lines follow, each line contains 2 integers x (0 <= x <= 100) and y (0 <= y <= 100), indicating the position of a horizontal dominoe. The dominoe occupies the grids of (x, y) and (x, y + 1).
Input ends with n = 0 and m = 0.

Output
For each test case, output the maximum number of remaining dominoes in a line.

Sample Input
2 3
0 0
0 3
0 1
1 1
1 3
4 5
0 1
0 2
3 1
2 2
0 0
1 0
2 0
4 1
3 2
0 0

Sample Output
4
6   题目大意：给你两种纸牌 ，一种水平放置共有n张 ，一种竖直放置共有m张。水平放置的纸牌占据点（x, y）和(x + 1 , y) ， 竖直放置的纸牌占据点(x , y) 和 (x , y + 1)。水平放置的牌之间不会重叠，竖直放置的牌之间也不会重叠，但是水平放置的牌和竖直放置的牌之间可能会重叠。让你拿走一些牌，使剩下的牌之间不会重叠并且数量最多，输出剩余的最大牌数。   解题思路：这是一道典型的二分图匹配问题，先是建图：水平放置的牌放入集合ho{}中，竖直放置的牌放入集合ve{}中，重叠的牌之间建立一条边。图建好后，求出图的最大匹配数 ， 然后由二分图的点独立数 = 顶点个数 - 匹配数 得到点独立数 即是答案。   请看代码：#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std ;
const int MAXN = 1111 ;
struct point
{
int x ;
int y ;
} ho[MAXN] , ve[MAXN];
int g[MAXN][MAXN] ;
int cx[MAXN] ;
int cy[MAXN] ;
int vis[MAXN] ;
int n , m ;
int path(int v)  // 匈牙利算法
{
int i ;
for(i = 0 ; i < m ; i ++)
{
if(g[v][i] == 1 && !vis[i])
{
vis[i] = 1 ;
if(cy[i] == -1 || path(cy[i]))
{
cy[i] = v ;
cx[v] = i ;
return 1 ;
}
}
}
return 0 ;
}
int pi(point a , point b) // 判断是否重叠
{
if(a.x == b.x && a.y == b.y)
return 1 ;
if(a.x == b.x && a.y == b.y + 1)
return 1 ;
if(a.x + 1 == b.x && a.y == b.y)
return 1 ;
if(a.x + 1 == b.x && a.y == b.y + 1)
return 1 ;
return 0 ;
}
void init()
{
while (cin >> n >> m)
{
if(n == 0 && m == 0)
break ;
memset(g , 0 , sizeof(g)) ;
memset(cx , -1 , sizeof(cx)) ; // 均初始化为-1 ，代表此点未被覆盖
memset(cy , -1 , sizeof(cy)) ;
int i , j ;
for(i = 0 ; i < n ; i ++)
{
scanf("%d%d" , &ho[i].x , &ho[i].y) ;
}
for(j = 0 ; j < m ; j ++)
{
scanf("%d%d" , &ve[j].x , &ve[j].y) ;
}
for(i = 0 ; i < n ; i ++)
{
for(j = 0 ; j < m ; j ++)
if(pi(ho[i] , ve[j]))
{
g[i][j] = 1 ; // 建图，注意这里千万不能写成 g[i][j] = g[j][i] = 1 !!
}
}
int ans = 0 ; // 记录匹配数
for(i = 0 ; i < n ; i ++)
{
if(cx[i] == -1)  // 如果 第i张 水平放置 的牌未被覆盖 ， 就从此点出发寻找增广路
{
memset(vis , 0 ,sizeof(vis)) ;
ans += path(i) ;
}
}
printf("%d\n" , n + m - ans) ;
}
}
int main()
{
init() ;
return 0 ;
}