給定一張二分圖,其最大匹配的方案不一定是唯一的。若任何一個匹配方案的匹配邊都包含(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;
}