匈牙利。

匈牙利算法,能在 \(O(Lm+R)\) 的复杂度处理二分图最大匹配的问题,其中 \(L\) 是左部点个数,\(R\) 是右部点个数,\(m\) 是边数,马良极短,吊打网络流。

首先我们有很多关键点能想到这个,要么是涉及到匹配,或者是涉及到需要取出一些环的问题,这个是一个极好的处理方式。

算法流程就是一种调整法,考虑就是对于一个点,暴力去尝试匹配,如果不能匹配上它就失配了。暴力的过程则分为两种:

  • 若该右部点没有匹配,直接匹配该右部点。

  • 若该右部点有匹配,尝试让原来匹配的那个点找到新的匹配。

以下是简短的代码:

#include<bits/stdc++.h>
using namespace std;
const int M=1e3+5;
int mtch[M];
int visT[M];
int n,m,e,x,y;
vector<int> E[M];
int ans;
bool dfs(int now,int tg){
	if(visT[now]==tg)return 0;
	visT[now]=tg;
	for(auto p:E[now])if(!mtch[p]||dfs(mtch[p],tg)){
		mtch[p]=now;
		return 1;
	}
	return 0;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m>>e;
	for(int i=1;i<=e;i++)cin>>x>>y,E[x].push_back(y);
	for(int i=1;i<=n;i++)ans+=dfs(i,i);
	cout<<ans;
	return 0;
}

其中 dfs 函数就是尝试匹配的最主要过程。递归的过程就是找新匹配的过程。为什么可以用 vis 数组?因为再次访问到只有两种情况,要么是再次尝试替换这个点,要么是替换成环,如果我们找到匹配就退出,那么这两种情况一定无解。

例题:CF387D George and Interesting Graph

George 喜欢图,因此它想让图更有趣。他认为有趣的图满足以下条件:

  • 没有重边;
  • 存在结点 \(v\)(称为中心),使得对于图中的任意结点 \(u\),都有边 \((u,v)\)\((v,u)\),注意自环 \((v,v)\) 也应该存在;
  • 除去中心外,每个点的入度和出度都恰好为 \(2\)

显然很少有图有趣,但 George 可以把图变得有趣:每次他可以增加一条边或者删除一条已经存在的边。

现在给出图 \(G\),George 想知道他最少做多少次操作可以使它变得有趣。

\(n\leq 500,m\leq 1000\)

枚举中心,剩下就变成尝试连出一些简单环使得一个点在且仅在一个简单环里,这个直接上二分图匹配就能求出最多有多少条已有边在这个系统里。

posted @ 2024-10-11 21:15  xingyu_xuan  阅读(26)  评论(4编辑  收藏  举报