P3386 【模板】二分图最大匹配 题解

写在前面:请管理员大大仔细审一下,不想再因为因举报核查被禁专栏了QAQ。谢谢!

题目传送锚点

在博客园食用更佳

前置芝士:网络最大流(文中使用了 dinic 算法)。不会的话请先出门右拐,慢走不送。

思路

虽然题目明确写了这道题是二分图最大匹配,但是我们需要把思路放开,所以这道题可以使用网络流。

众所周知,网络流的实现需要一个连通图,并且它里面应该包含源点和汇点。显然,这道题已经给出了很多个点和很多条边,但唯独没有给出源点与汇点。所以我们需要先建立超级源点和超级汇点:

  • 超级源点:由于(按照作者习惯)编号 \(1\) 到编号 \(n+m\) 都被占了,所以源点的编号为 \(0\)。然后再将源点和所有左部点用长为 \(1\) 的边连起来。至于为什么要这样做,后面会说;
  • 超级汇点:由于(按照作者习惯)编号 \(1\) 到编号 \(n+m\) 都被占了,所以汇点的编号为 \(n+m+1\)。然后再将汇点和所有右部点用长为 \(1\) 的边连起来。至于为什么要这样做,后面也会说。

顺便说一句,建图的时候,左部点和右部点之间的边长度随意,原因后面依然会说。

建完图后跑一遍网络流即可。

证明

思路已经说完了,现在说明一下唯一(也许吧)的疑点,那就是为什么要让所有与源点、汇点相连的边长都是 \(1\)

我们从题意入手。这道题是个二分图最大匹配,它有一个限制:所有点只能用一次,所有边只能用一次。所以,源点到左部点的边长为 \(1\),让每个左部点所在的路径只能走一次;汇点到右部点的边长为 \(1\),让每个右部点所在的路径也只能走一次。这里为 \(1\) 的边长,可以理解为一种限制,同时也满足了计数的需求。

顺便说一句,建图的时候,左部点和右部点之间的边长度随意,是因为该限制的已经限制了,该计数的已经能计了,所以长度只要不爆掉就行。

代码

最后就是代码时刻了。

#include<bits/stdc++.h>
#define int long long
using namespace std;

int n,m,e,s,t,x,y,z,ans,cnt=1,dist[1000000],now[1000000],head[1000000];

struct node
{
	int to;
	int value;
	int next;
}a[1000000];

void addedge(int x,int y,int z)
{
	a[++cnt].to=y;
	a[cnt].value=z;
	a[cnt].next=head[x];
	head[x]=cnt;
}

int bfs()
{
	for(int i=1;i<=t;++i) dist[i]=0x7fffffff;
	queue<int>q;
	q.push(s);
	now[s]=head[s];
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for(int i=head[x];i;i=a[i].next)
		{
			int y=a[i].to;
			if(a[i].value>0&&dist[y]==0x7fffffff)
			{
				q.push(y);
				now[y]=head[y];
				dist[y]=dist[x]+1;
				if(y==t) return 1;
			}
		}
	}
	return 0;
}

int dfs(int x,int sum)
{
	if(x==t) return sum;
	int k,res=0;
	for(int i=now[x];i&&sum;i=a[i].next)
	{
		now[x]=i;
		int y=a[i].to;
		if(a[i].value>0&&(dist[y]==dist[x]+1))
		{
			k=dfs(y,min(sum,a[i].value));
			if(k==0) dist[y]=0x7fffffff;
			a[i].value-=k;
			a[i^1].value+=k;
			res+=k;
			sum-=k;
		}
	}
	return res;
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie();
	cout.tie();
	cin>>n>>m>>e;
	for(int i=1;i<=e;++i)
	{
		cin>>x>>y;
		addedge(x,y+n,0x7fffffff);
		addedge(y+n,x,0);
	}
	t=n+m+1;
	for(int i=1;i<=n;++i)
	{
		addedge(s,i,1);
		addedge(i,s,0);
	}
	for(int i=n+1;i<t;++i)
	{
		addedge(i,t,1);
		addedge(t,i,0);
	}
	while(bfs())
	{
		ans+=dfs(s,0x7fffffff);
	}
	cout<<ans;
	return 0;
}

完结撒花。

posted @ 2025-07-09 17:01  cath20  阅读(16)  评论(0)    收藏  举报