- 给定一张(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;
}