算法设计与分析 6.3 棋盘取子

★题目描述

有一个N行M列的棋盘,上面摆放有K个棋子,现在想要尽可能的从棋盘上取下这些棋子

但是棋盘的每行每列至多允许取一个棋子,请问最多能取多少棋子?

★输入格式

输入的第一行三个数字N,M,K(1<=N,M<=100,1<=K<=M*M)$,表示棋盘与棋子数。

接下来K行每行两个数字a,b(1<=a<=N,1<=b<=M)代表一个位于a行b列的棋子。

★输出格式

输出一个整数表示最多能取的棋子数目。

★样例输入

2 2 4
1 1
1 2
2 1
2 2

★样例输出

2

★提示

★参考代码

/*
这题其实是二分图最大匹配数问题
二分图要求能够划分两个A,B子集,然后每个子集内部的点间没有联系。
也就是说二分图中的任何一条边必须一端在A子集内,另外一端在B子集内。

那么将棋盘的行 i 划分到A子集内,将棋盘的列 j 划分到B子集内
如果棋盘在第 i 行第 j 列有一颗棋子,那么 i、j构成一条边
这个构建出的图恰好满足二分图的条件

然后题目问的最多取棋子数就是问最大匹配数
*/
#include<bits/stdc++.h>
using namespace std;

int Match[501];
int Visit[501];

int H[501];
struct Edge{
	int end,nxt; 
}E[100001];

int idx=0;
void AddEdge(int u, int v){ //用邻接表方式记录存稀疏图 
    idx++;
    E[idx].end = v;
    E[idx].nxt = H[u];
    H[u] = idx;
}


int find(int x) {
    for(int i=H[x]; i!=0; i=E[i].nxt) {
        int e = E[i].end;
        if(!Visit[e]){
            Visit[e] = 1;
            if(Match[e]==0 || find(Match[e])) {
                Match[e] = x;
                return 1;
            }
        }
    }
    return 0;//自己中意的全部都被预定了。配对失败。
}


int main(){
    int n,m,k;
	scanf("%d%d%d",&n,&m,&k);
	
	int u,v;
	memset(H, 0, sizeof(H));
	while(k--){
		scanf("%d%d",&u,&v);
		AddEdge(u, v);
	}
	
	int res=0;
	memset(Match, 0, sizeof(Match));
	for(int i=1; i<=n; i++) {
        memset(Visit,0,sizeof(Visit));
        res += find(i);
    }
    
    printf("%d\n",res);
    return 0;
} 

posted on 2019-12-25 16:50  yejifeng  阅读(476)  评论(0编辑  收藏  举报

导航