解题思路
又是看得懂但不会做的一道题。。非常疑惑为什么总是会把暴力打挂。
“这题的转换思路是这样的:首先我们要求这个DAG的最长反链长度。~~balalala” 虽然讲得很好但我听了个寂寞。。
——————————————分割线—————————————
反链的定义是:“反链(antichain)是一个偏序集S的子集,其中任意两个元素不可比较”。在这道题中具体来说就是:当两个点互不可见时,它们才能处于一条反链中。
”根据Dilworth定理~~“,没听懂哈哈哈,所以,最后得出了得出了 最小链覆盖=最长反链长度。
〉〉证明.
现在我们要做的就是求出这个图的最小链覆盖(可以相交的),而最小链覆盖(不能相交的)=点数n-最大匹配数ans
证明就像这样:
我们假设有k条不相交的链覆盖了所有的点,长度(经过点数)分别为 l e n 1 , l e n 2 … … l e n k len1,len2……lenk len1,len2……lenk,而每一条链所经过的边数数即为 l e n 1 − 1 , l e n 2 − 1 … … l e n k − 1 len1-1,len2-1……lenk-1 len1−1,len2−1……lenk−1.
我们把它们加起来,因为覆盖了所有的点,所以len的和为 总点数n
而一条链在二分图中就对应着一个匹配,(有可能是一堆小链拼成一条长链,比如 1 − > 2 − > 3 − > 4 1->2->3->4 1−>2−>3−>4,匹配为 1 − 2 , 2 − 3 , 3 − 4 1-2 , 2-3 ,3-4 1−2,2−3,3−4)所以最小链覆盖为 :总点数n-最大匹配数 a n s ans ans
那么对应一个DAG,如何构造相应的二分图?
如下:
代码
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cmath>
using namespace std;
int n,m,x,y,ans,f[210][210],used[210],boy[210];
bool check(int x){
for(int i=1;i<=n;i++)
{
if(f[x][i]&&!used[i])
{
used[i]=1;
if(!boy[i]||check(boy[i]))
{
boy[i]=x;
return 1;
}
}
}
return 0;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
f[x][y]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(!f[i][j]&&i!=j)
for(int k=1;k<=n;k++)
if(f[i][k]&&f[k][j])
f[i][j]=1;
for(int i=1;i<=n;i++)
{
memset(used,0,sizeof(used));
if(check(i))
ans++;
}
printf("%d",n-ans);
}