Tutte 矩阵 求解一般图最大匹配

Tutte 矩阵 求解一般图最大匹配

证明之类的还是直接看 2017 年 IOI 国家集训队论文 《基于线性代数的一般图匹配》(杨家齐)。

一般图完美匹配

定义(Tutte 矩阵):对于一个无向图 G=(V,E),定义 G 的 Tutte 矩阵为一个 n×n 的矩阵 A~(G),其中

A~(G)={xi,jvivjEi<jxj,ivivjEi>j0otherwise

其中 xi,j 是两两不同,仅与边 vivj 有关的变量。

定理一:图 G 有完美匹配当且仅当 detA~(G)0

这个姑且还是学了一下怎么证。

一个图的环覆盖是说,用若干个环去覆盖 G 的所有节点,使得 G 的每个节点都恰好在一个环中。如果环长均为偶数,那么称其为一个偶环覆盖。

G 的每一个环覆盖都能和一个长度为 |V| 的置换所对应,具体地,对于一个排列 π,其对应的环覆盖为选出所有 vivπ(i) 的边。

引理一:图 G=(V,E) 有完美匹配当且仅当 G 存在偶环覆盖。

G 存在完美匹配,将所有匹配边看作一个二元环,即为一个偶环覆盖。

G 存在一个偶环覆盖,那么在每个偶环上间隔一条边取一条边,即可得到一个完美匹配。

故引理成立。

想要证得定理一,我们要说明 detA~(G)0 当且仅当 G 存在偶环覆盖。

根据行列式的定义有:

detA~(G)=π(1)sgn(π)A~(G)k,π(k)

考虑任意一个环覆盖对应的置换,将其中一个环反向,由于环的个数也就是置换的奇偶性没有改变,也就是其逆序对个数,sgn(π) 没有改变。

考虑这个环对乘积的贡献,根据 A~(G) 的定义可知,将一个环反向后,环上每一条边都贡献了一个 1,所以对一个奇环反向,其对应置换的权值会 ×(1),否则并不会变。

这告诉我们,对于每一个存在奇环的环覆盖,将其其中一个奇环反向,这两个环覆盖的权值恰好相反,则所有存在奇环的环覆盖都会互相抵消掉,不会对 detA~(G) 产生贡献。

由于 A~(G)xi,j 互不相同,所以若存在一个偶环覆盖,它一定不会被其他的偶环覆盖所消掉,则 detA~(G)0

综上所述 detA~(G)0 当且仅当 G 存在一个偶环覆盖,即 G 存在完美匹配。

现在问题变成了算 detA~(G),如果暴力做多项式操作复杂度将变得很大,由于最终不需要知道其取值为多少,仅需要判断最终多项式取值是否为 0

引理二 (Schwartz, Zippel):对于域 F 上的一个不恒为 0nd 度多项式 P(x1,x2,,xn)
r1,r2,,rnnF 中的独立选取的随机数,则:

Pr[P(r1,r2,,rn)=0]d|F|

证明还是看原论文。

人话:检查一个 n 次多项式是否恒为 0,选取模 p 意义下的若干随机数代入到各个元中,通过最终结果判断多项式是否恒为 0,概率是 np

所以选取较大的 p,或者多次随机,即可完成 detA~(G)0 的判断。

完美匹配方案构造

太难了,先咕着。

一般最大匹配

定理:图 G 的最大匹配的大小 ν(G)12rankA~(G)

求这个矩阵的秩也可以用在模 p 意义下给每个元随机权值的方式。

证明咕着。

贴个代码:

const int N=1010;
const ll mod=998244353;
inline void cdel(ll &x,ll y){x=(x-y<0)?(x-y+mod):(x-y);}
inline ll del(ll x,ll y){return (x-y<0)?(x-y+mod):(x-y);}
ll qpow(ll x,ll y){
	ll s=1;
	while(y){
		if(y&1)s=s*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return s;
}
int n,m;
ll v[N][N];
ll w[N*N];
pii e[N*N];
mt19937 rnd(0);
map<int,int>vis;
int Solve(){
	int c=0;
	for(int i=1;i<=n;i++){
		int p=0;
		for(int j=c+1;j<=n;j++)
			if(v[j][i]){
				p=j;
				break;
			}
		if(!p)continue;
		++c;
		swap(v[c],v[p]);
		ll rev=qpow(v[c][i],mod-2);
		for(int j=c+1;j<=n;j++){
			ll tmp=v[j][i]*rev%mod;
			for(int k=1;k<=n;k++)
				cdel(v[j][k],tmp*v[c][k]%mod);
		}
	}
	return c;
}
int main(){
	read(n,m);
	for(int i=1;i<=m;i++){
		int u,v;read(u,v);
		if(u>v)swap(u,v);
		e[i]=mp(u,v);
	}
	int ans=0;
	for(int o=1;o<=5;o++){
		for(int i=1;i<=m;i++)w[i]=rnd()%998244352+1;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				v[i][j]=0;
		for(int i=1;i<=m;i++){
			v[e[i].fi][e[i].se]=w[i];
			v[e[i].se][e[i].fi]=0;
			cdel(v[e[i].se][e[i].fi],w[i]);
		}
		int now=Solve()/2;
		++vis[now];
		if(vis[now]>vis[ans])
			ans=now;
	}
	cout << ans << '\n';
	return 0;
}

最大匹配方案构造

posted @   do_while_true  阅读(232)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?

This blog has running: 1845 days 1 hours 34 minutes 11 seconds

点击右上角即可分享
微信分享提示