Dilworth 定理
主要是做个笔记
DAG 最长反链 = 最小链覆盖
反链:反链上任意两个点 $(u,v)$ ,$u$ 不能到 $v$,$v$ 也不能到 $u$
最小链覆盖:选出若干可以相交的链,覆盖整张图,注意与“最小路径覆盖”(不能相交)的区别
最小链覆盖求法:先传递闭包,即做一遍 floyd 求出任意两点 $(u,v)$ 的连通关系,然后建立二分图,如果 $i$ 可以到 $j$,建立边 $(i,j+n)$,求二分图最大匹配,记匹配数为 $ans$,则答案为 $n-ans$
顺便,最小路径(不能相交)覆盖的求法就是不传递闭包直接匹配
DAG 最长反链 = 可以相交可以相交可以相交可以相交可以相交可以相交可以相交可以相交可以相交可以相交可以相交
bzoj1143 求最长反链
#include<bits/stdc++.h> #define LL long long using namespace std; inline int read() { int x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * f; } const int maxn = 100010; #define oo 2147483647 struct Dinic { int n,m,s,t; int head[maxn],cur[maxn],dis[maxn],nx[maxn]; queue<int> q; struct Edge { int from, to,caps; Edge(){} Edge(int _from, int _to, int _caps){from = _from, to = _to, caps = _caps;} }es[maxn]; Dinic(){memset(head,-1,sizeof(head));} void AddEdge(int u, int v, int w) { es[m] = Edge(u, v, w);nx[m] = head[u];head[u] = m;m++; es[m] = Edge(v, u, 0);nx[m] = head[v];head[v] = m;m++; } bool BFS() { memset(dis,0,sizeof(dis)); dis[t] = 1;q.push(t); while(!q.empty()) { int now = q.front();q.pop(); for(int i = head[now]; ~i; i = nx[i]) { Edge &e = es[i^1]; if(e.caps && !dis[e.from]) { dis[e.from] = dis[now] + 1; q.push(e.from); } } } return dis[s] > 1; } int DFS(int u,int a) { if(u == t || !a)return a; int flow, f = 0; for(int& i = cur[u]; ~i; i = nx[i]) { Edge &e = es[i]; if(dis[e.to] == dis[u] - 1 && (flow = DFS(e.to,min(e.caps,a)))) { f += flow; a -= flow; e.caps -= flow; es[i^1].caps += flow; if(!a)return f; } } return f; } int MaxFlow(int _s,int _t) { s = _s,t = _t; int ans = 0; while(BFS()) { memcpy(cur,head,sizeof(head)); ans += DFS(s,oo); }return ans; } } sol; int n,m,s,t; int mp[150][150]; int main() { n = read(),m = read(); for(int i=1,x,y;i<=m;i++)x = read(),y = read(),mp[x][y] = 1; for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)mp[i][j] |= (mp[i][k] & mp[k][j]); s = n+n+2,t = n+n+1; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(mp[i][j]) sol.AddEdge(i, j+n, 1); for(int i=1;i<=n;i++) sol.AddEdge(s, i, 1),sol.AddEdge(i+n, t, 1); int ans = n - sol.MaxFlow(s,t); cout << ans << endl; }