题目描述
说的是共有n个点,求前m个点与后n-m+1个点会有若干条路相连,求最大匹配程度。
如下图:最大匹配度则为3(1-6,2-4,3-5(7));
这个问题就很好地转化为了二分图最大匹配的问题(ljy:那不是最大流求解,从源点像前m个点连一条流量为1的边,原始的边连是要连,但是流量要改成INF,再从剩下的节点中连一条流量为1到超级汇,(如下图所示)就好了!!!),但是题目的关键是求匹配方案(zzy:一脸懵逼。。),所以我们要想一点妙招来解决这个东西。
妙招
我们来建立一个数组叫match[i](0<i<m)表示的事当前于i匹配的店是哪一个,没有时则为0。我们来模拟一下,s(源点)发送1流量到一号节点,一号节点找到了四号节点,然后标记match[1]=4;四号节点到t节点,碰到汇点,return。准备下一次流。s发送1流量到二号节点,二号节点只能找到四号节点,这时不用考虑四号节点的感受(一夫多妻。。)直接match[2]=4;发送1的流量给四号节点,这时四号节点发现自己不能连去汇点了(发现自己一夫多妻了),便通过与一号节点的反向弧把这1流量流回给一号节点(相当于拒绝了一号节点的求婚申请,同意了二号节点的)。一号节点便去继续找新的丈夫节点(说漏嘴了)。。。。。。之后的就不多说了
贴代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define INF 147483647
int n,m;
int first[200010];
struct edge{int y,next,d;};
edge s[400010];
int begin,end;
int len=1;
int match[100010];
int h[200010];
int f[200010];
int st=1,ed=2;
void ins(int x,int y,int d)
{
len++;
s[len].y=y;s[len].next=first[x];
s[len].d=d;first[x]=len;
}
int min(int x,int y)
{
return x<y?x:y;
}
bool bfs()
{
st=1,ed=2;
memset(h,0,sizeof(h));
h[begin]=1;
f[st]=begin;
while(st!=ed)
{
int x=f[st];
for(int i=first[x];i!=0;i=s[i].next)
{
int y=s[i].y;
if(h[y]==0 && s[i].d>0)
{
h[y]=h[x]+1;
f[ed]=y;
ed++;
if(ed==n+3) ed=1;
}
}
st++;
if(st==n+3) st=1;
}
if(h[end]!=0) return true;
return false;
}
int dfs(int x,int t)
{
if(x==end) return t;
int tot=0;
for(int i=first[x];i!=0;i=s[i].next)
{
if(tot==t) return t;
int y=s[i].y;
if(h[y]==h[x]+1)
{
int now=dfs(y,min(s[i].d,t-tot));
tot+=now;s[i].d-=now;s[i^1].d+=now;
if(now!=0 && x<=m && x>0)
match[x]=y;
}
}
if(tot==0) h[x]=0;
return tot;
}
int find_ans()
{
int ans=0;
while(bfs())
ans+=dfs(begin,INF);
return ans;
}
int main()
{
scanf("%d %d",&m,&n);
begin=0;end=n+1;
for(int i=1;i<=m;i++)
ins(begin,i,1),ins(i,begin,0);
for(int i=m+1;i<=n;i++)
ins(i,end,1),ins(end,i,0);
while(1)
{
int x,y;
scanf("%d %d",&x,&y);
if(x==-1 || y==-1) break;
ins(x,y,INF);ins(y,x,0);
}
int ans=find_ans();
if(ans==0)
printf("No Solution!");
else
printf("%d\n",ans);
for(int i=1;i<=m;i++)
if(match[i]!=0) printf("%d %d\n",i,match[i]);
}