分析:显然这是一道二分图的最大匹配问题.但是这道题问并不是问最大匹配值是多少,而是问有几个点去掉了是达到最大匹配的必须点。然而我们处理这个题建图的时候是把横坐标处理成二分图的左部,纵坐标处理成二分图的右部,可放点处理成左部到右部的一条边。那么所求就是问能成为最大匹配的所有匹配中那些边是必须边。
处理方法:我们先求出最大匹配A,然后枚举每条边,去掉后求最大匹配B,如果B==A那么说明当前边无关紧要,如果B<A,那么说明当前边为必须边.
两种存边方法: 邻接表、邻接矩阵。
邻接表:
#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <queue>
#include <stdlib.h>
using namespace std;
int pre[105],visit[105];
vector<int>way[105];
int l,r; //l,r就是被禁止的边.
int dfs(int step){
int i,next;
for(i=0;i<way[step].size();i++){
next=way[step][i];
if(visit[next] || step==l&&i==r)continue;
visit[next]=1;
if(pre[next]==-1 || dfs(pre[next])){
pre[next]=step;
return 1;
}
}
return 0;
}
int main()
{
int i,j,n,v,u,m,ans,k,rec,step,cishu=1;
while(cin>>n>>m){
for(i=0;i<105;i++)way[i].clear();
cin>>k;
for(i=0;i<k;i++){
cin>>u>>v;
way[u].push_back(v);
}
memset(pre,-1,sizeof(pre));
ans=0;rec=0;l=99999,r=99999;
for(i=1;i<=n;i++){
memset(visit,0,sizeof(visit));
if(dfs(i)==1)rec++;
}
for(i=1;i<=n;i++){
for(int d=0;d<way[i].size();d++){ //枚举每条边.
memset(pre,-1,sizeof(pre));
step=0;
l=i,r=d; //赋值给全局变量表禁止
for(j=1;j<=n;j++){
memset(visit,0,sizeof(visit));
if( dfs(j)==1)step++;
}
if(step<rec)ans++;
}
}
printf("Board %d have %d important blanks for %d chessmen.\n",cishu,ans,rec);
cishu++;
}
return 0;
}
邻接矩阵:
#include<iostream>
const int MAXN=110;
using namespace std;
int m,n,k;
int X[MAXN],Y[MAXN];
int cx[MAXN],cy[MAXN];
int map[MAXN][MAXN];
bool mark[MAXN];
int dfs(int u){
//考虑所以yi顶点v
for(int v=1;v<=n;v++){
//u与v邻接且没有被访问过
if(!mark[v]&&map[u][v]){
mark[v]=1;
//如果v没有匹配,或者v已经匹配了,但从cy[v]出发可以找到一条增广路
if(cy[v]==-1||dfs(cy[v])){
cy[v]=u;//把u匹配给v
cx[u]=v;//把v匹配给u
return 1;
}
}
}
return 0;
}
//匈牙利算法
int MaxMatch(){
int res=0;
memset(cx,-1,sizeof(cx));//从1匹配开始增广,将cx,cy各元素初始化为-1.
memset(cy,-1,sizeof(cy));
for(int i=1;i<=n;i++){
//从每个未盖点出发寻找增广路
if(cx[i]==-1){
memset(mark,false,sizeof(mark));
//每寻找到一条增广路可使匹配数增加1
if(dfs(i)){
res++;
}
}
}
return res;
}
int main(){
int _case=1;
while(~scanf("%d%d%d",&n,&m,&k)){
memset(map,0,sizeof(map));
for(int i=1;i<=k;i++){
scanf("%d%d",&X[i],&Y[i]);
map[X[i]][Y[i]]=1;
}
int ans=MaxMatch();//求二分图最大匹配的匈牙利算法
int count=0;
for(int i=1;i<=k;i++){
map[X[i]][Y[i]]=0;//试着去掉每一条边
int res=MaxMatch();
map[X[i]][Y[i]]=1;//恢复
if(res<ans)count++;//如果比先前求出的小,说明是重要点
}
printf("Board %d have %d important blanks for %d chessmen.\n",_case++,count,ans);
}
return 0;
}