题意:给你一张n个点的DAG,最大化选择的点数,是点之间两两不可达。
要从Dilworth定理说起。
Dilworth定理是定义在偏序集上的,也可以从图论的角度解释。偏序集中两个元素能比较大小,则在图中连一条有向边。
定义反链为一个点集,满足集合中的点两两不可达。
Dilworth定理:最小路径覆盖=最长反链。
证明:http://vfleaking.blog.163.com/blog/static/1748076342012918105514527/
例子:NOIP1999第二问:给定一个数列a,将其分成若干个子序列,使每个子序列都是下降子序列,并最小化分的子序列个数。
对于所有i<j且a[i]>a[j],从i向j连边,则问题转化成求图的最小路径覆盖,根据Dilworth定理进一步转化为求最长反链,而这里的最长反链即是最长不降子序列的长度。所以这题只要求一个最长不降子序列即可。
回到这道题,就是要求一个最长反链。这里任意两个可达的点之间都需要连一条边,所以需要求传递闭包。
然后通过Dilworth定理转化为求DAG的最小路径覆盖,这就是二分图匹配的经典问题了。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 using namespace std; 6 7 const int N=110; 8 int n,m,u,v,ans,lk[N]; 9 bool mp[N][N],a[N][N],vis[N]; 10 11 bool find(int x){ 12 rep(i,1,n) if (!vis[i] && mp[x][i]){ 13 vis[i]=1; 14 if (!lk[i] || find(lk[i])) { lk[i]=x; return 1; } 15 } 16 return 0; 17 } 18 19 int main(){ 20 freopen("bzoj1143.in","r",stdin); 21 freopen("bzoj1143.out","w",stdout); 22 scanf("%d%d",&n,&m); 23 rep(i,1,m) scanf("%d%d",&u,&v),mp[u][v]=1; 24 rep(k,1,n) rep(i,1,n) rep(j,1,n) mp[i][j]|=mp[i][k]&mp[k][j]; 25 rep(i,1,n) rep(j,1,n) if (i!=j && mp[i][j]) a[i][j]=1; 26 rep(i,1,n){ 27 memset(vis,0,sizeof(vis)); 28 if (find(i)) ans++; 29 } 30 printf("%d ",n-ans); 31 return 0; 32 }