思路:
用网络流跑二分图的最大匹配,先将H公司第个人的编号变为
,然后
表示
公司和
公司的两个人有关系,待匹配。残余网络中非匹配边为从左部到右部的有向边,的边权为
,匹配边为从右部到左部的有向边,边权为
。
非匹配边:,
表示边的容量
匹配边:
因为要求不可行的边的数量,即不是可行边和必须边的边;先跑一遍最大匹配
最大匹配时,必须边一定会被包括,所以如果,那么它就不是必须边,如果边上的点又不在一个强连通分量内,那么也不是可行边,而是不可行边。
MyCode:
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e4+7,maxm=2e5+4e4+7;
int head[maxn],Next[maxm],to[maxm],edge[maxm],tot=1,d[maxn],s,t,now[maxn];
queue<int>q;
void add(int x,int y,int z) {
to[++tot]=y; Next[tot]=head[x]; head[x]=tot; edge[tot]=z;
to[++tot]=x; Next[tot]=head[y]; head[y]=tot; edge[tot]=0;
}
bool bfs() {
memset(d,0,sizeof d);
while(q.size()) q.pop();
q.push(s); d[s]=1; now[s]=head[s];
while(q.size()) {
int x=q.front(); q.pop();
for(int i=head[x],y;i;i=Next[i]) {
if(edge[i] && !d[y=to[i]]) {
q.push(y);
now[y]=head[y];
d[y]=d[x]+1;
if(y==t) return 1;
}
}
}
return 0;
}
int dinic(int x,int flow) {
if(x==t) return flow;
int rest=flow,k,i,y;
for(i=now[x];i && rest; i=Next[i]) {
if(edge[i] && d[y=to[i]] == d[x] + 1) {
k=dinic(y,min(rest,edge[i]));
if(!k) d[y]=0;
edge[i]-=k; edge[i^1]+=k;
rest-=k;
}
}
now[x]=i;
return flow - rest;
}
int dfn[maxn],low[maxn],ins[maxn],c[maxn],stk[maxn],a[maxm],b[maxm],num,cnt,top;
vector<int>ans;
void tarjan(int x,int in_edge) {
dfn[x]=low[x]=++num;
stk[++top]=x; ins[x]=1;
for(int i=head[x],y;i;i=Next[i]) {
if(!edge[i]) continue;
if(!dfn[y=to[i]]) {
tarjan(y,i);
low[x]=min(low[x],low[y]);
}
else if(ins[y])
low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]) {
++cnt; int y;
do{
y=stk[top--]; ins[y]=0;
c[y]=cnt;
}while(x!=y);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m,T;
cin>>n>>m>>T;
for(int i=1,x,y;i<=T;++i) {
cin>>x>>y; y+=n;
a[i]=x; b[i]=y; add(x,y,1);
}
s=0,t=n+m+1;
for(int i=1;i<=n;++i) add(s,i,1);
for(int i=n+1;i<=m+n;++i) add(i,t,1);
while(bfs())
while(dinic(s,0x3f3f3f3f));
for(int i=0;i<=n+m+1;++i) if(!dfn[i])
tarjan(i,0);
for(int i=1;i<=T;++i) if(edge[i<<1] && c[a[i]]!=c[b[i]])
ans.emplace_back(i);
cout<<ans.size()<<'\n';
for(int i:ans) cout<<i<<' ';
cout<<'\n';
return 0;
}

京公网安备 11010502036488号