多米诺(codevs 3052)

题目描述 Description

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

输入描述 Input Description

第一行有两个用空格隔开的正整数M和N。

    第二行有一个正整数K,表示共有K个小正方形不能使用。输入数据保证K<=M*N。

    以下K行每行有两个用空格隔开的数X和Y,表示第X行的第Y个小正方形不能使用。

输出描述 Output Description

输出最多能放多少个多米诺骨牌。

样例输入 Sample Input

3 3

2

1 1

2 2

样例输出 Sample Output

3

数据范围及提示 Data Size & Hint

对于30%的数据,M=1;

    对于50%的数据,M<=2;

    对于70%的数据,M<=3;

    对于100%的数据,M<=50,N<=50。

/*
  二分图匹配问题
  问题是从一坨方格关系中选出尽量多的关系,使每个方格只选一次。 
  通过观察我们可以发现,横坐标加纵坐标是偶数的方格只能和奇数的方格匹配,所以就变成了二分图的匹配问题。 
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#define N 51
using namespace std;
int g[N][N],n,m,p,head[N*N],cnt;
int used[N*N],belong[N*N];
struct node{
    int v,pre;
};node e[N*N*2];
void add(int x,int y){
    e[++cnt].v=y;
    e[cnt].pre=head[x];
    head[x]=cnt;
}
bool find(int i){
    for(int j=head[i];j;j=e[j].pre){
        if(!used[e[j].v]){
            used[e[j].v]=1;
            if(!belong[e[j].v]||find(belong[e[j].v])){
                belong[e[j].v]=i;
                return true;
            }
        }
    }
    return false;
}
int main(){
    freopen("jh.in","r",stdin);
    scanf("%d%d%d",&n,&m,&p);
    for(int i=1;i<=p;i++){
        int x,y;scanf("%d%d",&x,&y);
        g[x][y]=1;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if((i+j&1)||g[i][j])continue;
            if(!g[i-1][j]&&i-1>=1)add((i-1)*m+j,(i-2)*m+j);
            if(!g[i+1][j]&&i+1<=n)add((i-1)*m+j,i*m+j);
            if(!g[i][j-1]&&j-1>=1)add((i-1)*m+j,(i-1)*m+j-1);
            if(!g[i][j+1]&&j+1<=m)add((i-1)*m+j,(i-1)*m+j+1);
        }
    }
    int tot=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if((i+j&1)||g[i][j])continue;
            memset(used,0,sizeof(used));
            if(find((i-1)*m+j))tot++;
        }
    }
    printf("%d",tot);
    return 0;
}
原文地址:https://www.cnblogs.com/harden/p/6052588.html