二分图必须边和可行边的判定定理(完备匹配)
一 (x,y)是必须边 当且仅当以下两个条件都满足
1.(x,y)当前是匹配边
2. 删除边(x,y)后,不能找到另一条从x到y的增广路。
二 (x,y)是可行边 ,当且仅当满足以下两个条件之一
1.(x,y)当前是匹配边
2.(x,y)是非匹配边,设当前x与v匹配,y与u匹配,连接边(x,y)后,节点u,v失去匹配。必须能找到一条从u到v的增广路。
G是二分图 那么把G中非匹配边看作从左部到右部的有向边,把匹配边看作从右部到左部的有向边,构成一张新的有向图,记作G1,那么G中从x到y有增广路,等价于G1存在从x到y路径
二分图必须边和可行边的判定定理可改写为(完备匹配)
一 (x,y)是必须边 当且仅当以下两个条件都满足
1.(x,y)当前是匹配边
2. x,y两点在G1属于不同强连接分量
二 (x,y)是可行边 ,当且仅当满足以下两个条件之一
1.(x,y)当前是匹配边
2.x,y两点在G1属于相同强连接分量
二分图必须边和可行边的判定定理可改写为(非完备匹配)
一 (x,y)是必须边 当且仅当以下两个条件都满足
1.(x,y)流量为1
2. x,y两点在残量网络属于不同强连接分量
二 (x,y)是可行边 ,当且仅当满足以下两个条件之一
1.(x,y)当前是匹配边
2.x,y两点在残量网络属于相同强连接分量
其中残量网络是在Dinic算法之后对剩余的容量为1的边建立的新图
其中剩余容量为0的边代表流量为1
题目大意
求非完备二分图不可行边个数
题目思路
给二分图加一个源点汇点后 使用最大流Dinic模板求出每条边的流量和残量网络 然后再用tarjan模板求出强连通分量
逐边判断是否为非可行边
代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
const int M=5e6+10;
int n, m, t;
bool vis[N];
vector<int> mp[N];
vector<int> ans;
struct Dinic{//Dinic模板
int cnt = 1, s, t, INF = 1 << 29;
int head[N], d[N], now[N];
struct Edge{int nxt, to, val;} ed[M];
queue<int>q;
void add(int u, int v, int w){
ed[++ cnt] = (Edge){head[u], v, w}; head[u] = cnt;
ed[++ cnt] = (Edge){head[v], u, 0}; head[v] = cnt;
}
bool bfs(){
while(!q.empty()) q.pop();
memset(d, 0, sizeof(d));
q.push(s); d[s] = 1; now[s] = head[s];
while(!q.empty()){
int u = q.front(); q.pop();
for(int i = head[u]; i; i = ed[i].nxt){
int v = ed[i].to, w = ed[i].val;
if(!d[v] && w){
now[v] = head[v];
d[v] = d[u] + 1;
if(v == t) return true;
q.push(v);
}
}
}
return false;
}
int dinic(int u, int flow){
if(u == t) return flow;
int rest = flow, i;
for(i = now[u]; i && rest; i = ed[i].nxt){
int v = ed[i].to, w = ed[i].val;
if(w && d[v] == d[u] + 1){
int k = dinic(v, min(rest, w));
if(!k) d[v] = 0;
ed[i].val -= k;
ed[i ^ 1].val += k;
rest -= k;
}
}
now[u] = i;
return flow - rest;
}
void work(int S, int T){
s = S, t = T;
int flow = 0, maxflow = 0;
while(bfs())
while(flow = dinic(s, INF)) maxflow += flow;
for(int u = s; u <= t; u ++)//建残量网络新图。
for(int i = head[u]; i; i = ed[i].nxt){
int v = ed[i].to, w = ed[i].val;
if(w) mp[u].push_back(v);
else vis[i] = true;
//在残量网络中,容量为 0 说明流量为 1,反之同理。
}
}
} G;
int top, tot, num;
int dfn[N], low[N], st[N], c[N];
void tarjan(int u){//tarjan模板
dfn[u] = low[u] = ++ num;
st[++ top] = u;
for(int i = 0; i < mp[u].size(); i ++){
int v = mp[u][i];
if(!dfn[v])
tarjan(v), low[u] = min(low[u], low[v]);
else if(!c[v])
low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u]){
int v; tot ++;
do{
v = st[top --];
c[v] = tot;
}while(v != u);
}
}
int S,T;
int main()
{
cin>>n>>m>>t;
S=0;
T=n+m+1;
int x,y;
for(int i=1;i<=t;i++)
{
scanf("%d %d",&x,&y);
G.add(x,y+n,1);
}
for(int i=1;i<=n;i++)G.add(S,i,1);
for(int i=1;i<=m;i++)G.add(i+n,T,1);
G.work(S,T);
for(int i=S;i<=T;i++)
{
if(!dfn[i])tarjan(i);
}
for(int i=2;i<=2*t+1;i+=2)
{
int u=G.ed[i].to,v=G.ed[i^1].to;
if(vis[i]==0&&c[u]!=c[v])ans.push_back(i/2);
}
cout<<ans.size()<<endl;
for(int i=0;i<ans.size();i++)cout<<ans[i]<<" ";
cout<<endl;
return 0;
}