给定一张二分图,其最大匹配的方案不一定是唯一的。若任何一个匹配方案的匹配边都包含(x,y),则称(x,y)为二分图最大匹配的必须边(可以理解为如果不匹配这条边的话二分图最大匹配就少一条)。若(x,y)至少属于一个最大匹配的方案,则称(x,y)为二分图最大匹配的可行边(可以理解为这条边如果不匹配的话还可以再用其两个点再匹配出其他边,使得最大匹配不变)。
必须边判定条件:(x,y)流量为1,且在残量网络上属于不同的强连通分量。
可行边判定条件:(x,y)流量为1,且在残量网络上属于相同的强连通分量。
主要是求必须边。
思路:用Dinic算法求最大流,Tarjan算法求强连通分量。
如果左边有n个匹配点,右边有m个匹配点,共有k个匹配边,则时间复杂度为o(k*sqrt(n+m))。
大胆猜想证明结论:如果一堆点属于一个强连通分量,忽略原点和汇点所连的边,只看二分图里面的边,一定是要不从左到右,要么从右到左,如果是强连通分量则从左到右和从右到左的边是一样的,这个强连通分量的各边变方向,会导致最大匹配数不变,因为从左到右和从右到左的边数量一样,怎么变方向也无所谓,所以如果想当独一无二的匹配边,即必须边,则左右两个点必须不在一个强连通分量上。
HDU3026
这个题数组大小不好开,需要思考,而且开小了返回WA,由于多组输入需要初始化不能用memset,因为会T,只能for循环初始化。
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int INF=1<<29;
const int MAX_N=20100;
const int MAX_M=250100;
const int MAX_K=101000;
int head[MAX_N],edge[MAX_M],Next[MAX_M],ver[MAX_M],d[MAX_N],now[MAX_M];
int n,m,s,t,tot;
void Add(int x,int y,int z){
ver[++tot]=y;edge[tot]=z;Next[tot]=head[x];head[x]=tot;
ver[++tot]=x;edge[tot]=0;Next[tot]=head[y];head[y]=tot;
}
bool bfs(){
for(int i=1;i<=n+m+2;i++)
d[i]=0;
queue<int>q;
q.push(s);
d[s]=1;
now[s]=head[s];
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=head[x];i;i=Next[i]){
int y=ver[i];
if(edge[i]&&!d[y]){
q.push(y);
now[y]=head[y];
d[y]=d[x]+1;
if(y==t)
return true;
}
}
}
return false;
}
int dfs(int x,int flow){
if(x==t)
return flow;
int res=flow,k,i;
for(i=now[x];i&&res;i=Next[i]){
int y=ver[i];
if(edge[i]&&d[y]==d[x]+1){
k=dfs(y,min(res,edge[i]));
if(!k)
d[y]=0;
edge[i]-=k;
edge[i^1]+=k;
res-=k;
}
}
now[x]=i;
return flow-res;
}
int dinic(){
int maxflow=0;
int flow=0;
while(bfs()){
while(flow=dfs(s,INF))
maxflow+=flow;
}
return maxflow;
}
int ans;//强连通分量的个数
int low[MAX_N],num[MAX_N],cnt;
int stack[MAX_N],top;
int vis[MAX_N];
void dfs_(int now){
int i;
stack[top++]=now;
low[now]=num[now]=++cnt;
for(i=head[now];i;i=Next[i]){
int to=ver[i];
if(!edge[i])
continue;
//cout<<now<<" "<<to<<" to\n";
if(!num[to]){
dfs_(to);
low[now]=min(low[now],low[to]);
}
else if(!vis[to])
low[now]=min(low[now],num[to]);
}
if(low[now]==num[now]){
ans++;
while(1){
int to=stack[--top];
vis[to]=ans;
if(now==to)
break;
}
}
}
void tarjan(){
int i;
ans=cnt=top=0;
//memset(vis,0,sizeof(vis));
//memset(num,0,sizeof(num));
//memset(low,0,sizeof(low));
for(i=1;i<=n+m+2;i++){
vis[i]=num[i]=low[i]=0;
}
for(i=1;i<=n+m+2;i++){
if(!num[i])
dfs_(i);
}
}
int ax[MAX_K],ay[MAX_K],ex[MAX_K];
int main(void){
int k,i,x,y;
int T=0;
while(scanf("%d%d%d",&n,&m,&k)!=EOF){
T++;
s=n+m+1;t=n+m+2;
tot=1;
for(i=1;i<=n+m+2;i++)
head[i]=0;
for(i=0;i<k;i++){
scanf("%d%d",&x,&y);
ax[i]=x;
ay[i]=y+n;
ex[i]=tot+1;
Add(x,y+n,1);
}
for(i=1;i<=n;i++)
Add(s,i,1);
for(i=n+1;i<=n+m;i++)
Add(i,t,1);
int abc=dinic();
tarjan();
int res=0;
for(i=0;i<k;i++){
if(!edge[ex[i]]){
if(vis[ax[i]]!=vis[ay[i]]){
res++;
}
}
}
printf("Board %d have %d important blanks for %d chessmen.\n",T,res,abc);
}
return 0;
}