二分图的必须边和可行边

给定一张二分图,其最大匹配的方案不一定是唯一的。若任何一个匹配方案的匹配边都包含(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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值