二分图相关,以及简单建图的一些细节,矩阵技巧

前言

主要说两道题。

二分图最大匹配

可以用匈牙利算法O(nm)解决,可以用dinic算法O(n√m)解决,更快的话可以预流推进。
一般来说匈牙利足够了。
然后说一下匈牙利的代码:(10个月没写过dinic了,有空复习一下)

#include<iostream>
#include<vector>
using namespace std;
int n,m,e;
vector<vector<int>> a;
int vis[505],match[505];
int ans;
bool dfs(int u) {
	for(auto&v:a[u]) {
		if(vis[v]) continue;
		vis[v]=true;
		if(!match[v]||dfs(match[v])) {
			match[v]=u;
			return true;
		}
	}
	return false;
}
int main() {
	cin>>n>>m>>e;
	for(int i=0;i<=n;i++) a.push_back({});
	while(e--) {
		int u,v;
		cin>>u>>v;
		a[u].push_back(v);
	}
//	for(int i=1;cout<<i<<':',i<=n;i++,cout<<endl)
//		for(auto&j:a[i])
//			cout<<j<<' ';
	for(int i=1;i<=n;i++) {
		for(auto&j:vis) j=0;
		if(dfs(i))
			ans++;
	}
	cout<<ans;
	return 0;
}

主要的细节就是,注意vis数组是每进行一轮dfs,都要清空一次,这意味着每一个左侧点可以在不同的轮中匹配同一个右侧节点,而在同一轮中不可以。

这是由于,在每加入一个新的节点匹配,所有左侧节点最多重新匹配一次,如果不标记则会陷入死循环。而在多轮中,由于节点u原来出让的节点v的match[x]还可能可以将节点v再次出让给节点u,进而完成匹配,所以可以在不同轮中重复匹配一个节点。

简单建图与矩阵技巧

例题1
结论很好猜,难的是证明。先证明一下这道题目的结论:

最终状态是(1,1)(2,2)…(n,n)都有一个点
我们把点看成匹配边的话,就是每行和每列都做到了匹配
换言之就是N个行和N个列都有匹配时,一定能转换成最终状态
所以就如S向每行所对应的点连边,每列所对应的点向T连边
每个1的块就是某行和某列的边
再逆过来转换到初始状态,我们发现交换行本质就是交换S向这两行连的边,所以匹配数不变
同理交换列也是
所以只要按照最初状态建二分图跑最大流,就是可能的最终状态的最大流
当且仅当最大流是N的时候可以转换到最终状态
——Night_Aurora

矩阵技巧

然后这道题用到了一个经典的关于矩阵的结论:
对于矩阵:
行交换操作:选择矩阵的任意两行,交换这两行
列交换操作:选择矩阵的任意两列,交换这两列
交换两行时,这两行内的数字相对位置关系不变。
交换两列时,这两列内的数字相对位置关系不变。
任意两次行列操作都不互相影响,归纳可以得出,任何次数的行列都操作互不影响。
也就是说,可以先交换行,再交换列。
同样用到类似技巧的还有这道题
此类矩阵问题建图,主要涉及三个方面:连通性(主要是SCC),二分图和网络流。

后话

于是皆大欢喜。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值