【洛谷4298】[CTSC2008] 祭祀(Dilworth定理+二分图匹配)

点此看题面

  • 给定一张(n)个点(m)条边的(DAG),求最多能选出多少个点,使得两两无法到达。
  • 进一步,给出任意一组最优构造方案,并询问每个点是否可能出现在一组最优构造方案中。
  • (nle100,mle1000)

(Dilworth)定理

精简概括:对于任意有限偏序集,最大反链长等于最小链划分

其中,指的是一串元素(x_{1sim n})满足(x_1le x_2le...le n)(le)为这个偏序集中的偏序关系。

反链指的是一堆元素(x_{1sim n}),它们两两之间无法比较。

二分图匹配解最小路径覆盖

对于这道题,这个偏序关系可以定义为:(xle y)当且仅当(x)能到达(y)。(可以(Floyd)预处理出)

然后发现题目要求的就是最大反链长,那就可以转化为最小链覆盖,放在这题中就变成了最小路径覆盖

经典的二分图匹配问题,把每个点视作一个出点和一个入点,对于一条边(x ightarrow y)(x)的出点向(y)的入点连边。

答案上界是点数,而每匹配成功一条边就可以少用一条链,因此答案就是点数-二分图最大匹配

能否出现在最优构造方案中

发现这里的反链其实等价于独立集

所以判断一个点能否出现在最优构造方案中,只要删去它及其相邻点,然后用之前的方法跑一遍看看新的最大反链长是否恰好比原先小(1),是则能够出现在最优构造方案中。

而要给出一组最优构造方案,只要连续使用上面的结论,每确定一个点可能出现在最优构造方案中将它选中,保留它产生的影响(即删去它的相邻点)继续枚举。

代码:(O(n^4))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100
#define M 1000
using namespace std;
int n,m;bitset<N+5> f[N+5];
int p[N+5],s[N+5],vis[N+5];I bool Match(CI x,CI ti)//二分图匹配
{
	for(RI i=1;i<=n;++i) if(f[x].test(i)&&
		!p[i]&&vis[i]^ti&&(vis[i]=ti,!s[i]||Match(s[i],ti))) return s[i]=x;
	return 0;
}
I int Calc() {RI i;for(i=1;i<=n;++i) s[i]=vis[i]=0;//清空
	RI t=0;for(i=1;i<=n;++i) !p[i]&&(t+=!Match(i,i));return t;}//点数-二分图最大匹配,即匹配失败点数
int main()
{
	RI i,j,x,y;for(scanf("%d%d",&n,&m),i=1;i<=m;++i) scanf("%d%d",&x,&y),f[x].set(y);
	for(j=1;j<=n;++j) for(i=1;i<=n;++i) f[i].test(j)&&(f[i]|=f[j],0);//Floyd
	RI k=Calc(),t=k;for(printf("%d
",k),i=1;i<=n;++i)
	{
		if(p[i]) {putchar('0');continue;}//如果已经被删掉
		for(j=1;j<=n;++j) p[j]+=i==j||f[j][i]||f[i][j];if(Calc()==t-1) {--t,putchar('1');continue;}//如果在最优构造方案中就选中
		for(j=1;j<=n;++j) p[j]-=i==j||f[j][i]||f[i][j];putchar('0');//没选中就撤销影响
	}
	for(putchar('
'),i=1;i<=n;++i)
	{
		for(j=1;j<=n;++j) p[j]=i==j||f[j][i]||f[i][j];putchar(48|(Calc()==k-1));//独立判断是否能在最优构造方案中
	}return 0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4298.html