算法设计与分析 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;
}