码迷,mamicode.com
首页 > 其他好文 > 详细

[BZOJ1143]祭祀

时间:2018-02-26 15:14:47      阅读:137      评论:0      收藏:0      [点我收藏+]

标签:gpo   没有   转化   include   不可   main   body   传递闭包   可以转化   

这题水很深...

题目给了一个有向无环图,要求找出最多的点且这些点中不存在两个点使得它们之间有路径

如果$x$能到$y$,那么$x,y$只能选其中一个,所以连上一条边$(x,y)$不改变答案(其实是在找传递闭包)

这时可以转化一下题目:给出一个偏序集,问最大反链长度

Dilworth定理:偏序集的最大反链长度=偏序集划分为链的最少链数

这里有一篇博客写得非常好,这里引用+删减

用数学归纳法

假设偏序集为$S$且$n=\left|S\right|$,对于$n\geq 2$,假设对所有的$m\lt n$命题成立

取出$S$中的一个极大元$x$,令$S‘=S-\left\{x\right\}$

对于$S‘$,命题成立,假设它被划分成$k$条链,取$B$为每条链的最大元组成的偏序集,它是一个最大反链

①若$x$与$B$中任一元素不可比,那么它使最少链数$+1$,使最大反链长度$+1$

②若$x$与$B$中某一元素可比,因为是极大元,所以它不改变最少链数和最大反链长度

所以我们成功地把问题转化为求最小链划分

求偏序集的最小链划分是一个经典问题,可以参考这篇博客,这里引用+删减

把每个点$x$拆成$\left(x_1,x_2\right)$,对于每条原图中的边$(x,y)$,在新图中连边$\left(x_1,y_2\right)$

设$match$为新图的最大匹配数,答案$=n-match$

原理是匹配一条边相当于连接两条链(答案$-1$),因为是划分(链不可相交)所以对应到新图中匹配边不可相交

然后就做完了

总的过程:先用Warshall算法求出传递闭包,再建二分图用匈牙利算法跑匹配,答案是节点数减去匹配数

代码很短,但背后蕴含的思想较为恐怖

原来离散数学前几章不是一点用都没有啊

#include<stdio.h>
#include<string.h>
bool g[110][110],v[110];
int n,link[110];
bool hungary(int x){
	for(int i=1;i<=n;i++){
		if(g[x][i]&&!v[i]){
			v[i]=1;
			if(link[i]==-1||hungary(link[i])){
				link[i]=x;
				return 1;
			}
		}
	}
	return 0;
}
int match(){
	int i,s=0;
	memset(link,-1,sizeof(link));
	for(i=1;i<=n;i++){
		memset(v,0,sizeof(v));
		if(hungary(i))s++;
	}
	return s;
}
int main(){
	int m,i,j,k;
	scanf("%d%d",&n,&m);
	while(m--){
		scanf("%d%d",&i,&j);
		g[i][j]=1;
	}
	for(k=1;k<=n;k++){
		for(i=1;i<=n;i++){
			for(j=1;j<=n;j++)g[i][j]=g[i][j]||(g[i][k]&&g[k][j]);
		}
	}
	printf("%d",n-match());
}

[BZOJ1143]祭祀

标签:gpo   没有   转化   include   不可   main   body   传递闭包   可以转化   

原文地址:https://www.cnblogs.com/jefflyy/p/8472860.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!