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题意 : 有一种1*2格的牌,能水平放,和竖直放; 给出n个水平放的牌,并且是不会重叠覆盖的,给出m个竖直的牌,也不会覆盖. 但是水平和竖直的会覆盖, 有覆盖的牌 通过丢弃一些牌 ,使得所有牌都没有被覆盖,问不被覆盖的最大的牌数.使用二分匹配因为水平方向 任意两个牌不会覆盖 ,即两点不会相交.竖直也一样, 所以把水平看成X集合,竖直看成Y集合 . 两点连线相当于两个牌互相覆盖 . 找出这个图的最大匹配 ,每个匹配是 互相覆盖的两个牌,所以,一个匹配,即一条边,只能留一个牌,所以相当于丢弃的牌=最大匹配 .说明一下,图中不是最大匹配的边,就是独立的一张牌,所以,不用丢弃. 所以答案为 总牌数-最大匹配 .每个牌子只给一个左边(x,y),则这个牌占的格子是(x,y)(x+1,y)或者竖直的(x,y)(x,y+1);#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <cmath> #include <vector> #define maxn 1010 using namespace std; int N,M; struct node { int x,y; } p1[1010],p2[1010]; int lef[maxn]; bool T[maxn]; int cnt; vector<int> g[1010]; bool match(int u) { int i,j; for(i=0; i<g[u].size(); i++) { int v = g[u][i]; if (!T[v]) { T[v] = true; if (lef[v] == -1 || match(lef[v])) { lef[v] = u; return true; } } } return false; } int solve() { memset(lef, -1, sizeof(lef)); int ans = 0; int u; for(u = 1; u <= N; u++) { memset(T, 0, sizeof(T)); if(match(u)) { ans++; } } return ans; } bool judge(int a,int b) //判断两点是否练成一条边,即两个牌是否覆盖 { if(p1[a].x==p2[b].x&&p1[a].y==p2[b].y) { return true; } if(p1[a].x==p2[b].x&&p1[a].y==p2[b].y+1) { return true; } if(p1[a].x+1==p2[b].x&&p1[a].y==p2[b].y) { return true; } if(p1[a].x+1==p2[b].x&&p1[a].y==p2[b].y+1) { return true; } return false; } void build() { int i,j; for(i=1; i<=N; i++) { for(j=1; j<=M; j++) { if(judge(i,j)) { g[i].push_back(j); } } } } int main() { // freopen("input.txt","r",stdin); int i,j; while(scanf("%d %d",&N,&M)==2&&N+M) { for(i=1; i<=N; i++) { scanf("%d %d",&p1[i].x,&p1[i].y); g[i].clear(); } for(i=1; i<=M; i++) { scanf("%d %d",&p2[i].x,&p2[i].y); } build(); int temp=solve(); printf("%d\n",N+M-temp); } return 0; }