题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3795
http://vjudge.net/contest/view.action?cid=52151#problem/H
浙大月赛题
1.题意:
有N个人,M条边,边(u,v)表示u的年龄不小于v,若把这N个人分为很多组,要求每一组中的年龄无法相互比较,求组数最小值。
2.题解:
(1)掌握强联通、偏序集(链和反链)的基础知识。
(2)设(X,<=)是一个有限偏序集。反链是X的一个子集A,它的任意两个元素都不可比。相比之下,链是X的一个子集C,他的每个元素都可比。本题就是求反链数的最小值。
(3)定理:设(X,<=)是一个有限偏序集,而设r是的链的最大大小。则X可以被划分成r个反链,但不能划分成少于r个反链。
证明:假如已经找到了p个反链,自然就可以找到>=p个反链,只要把当前其中任意一个大于2的反链拆成2个反链,就可行。所以只要证明X可以分为r个反链即可。令X1=X,并设A1是X的极小元的集合。从X1中删除A1个元素的到X2,对于X2中的每一个元素,存在A1的某个元素在这个偏序之下在这个元素下方(同一条链上的元素有偏序关系,每个非极小元都可以找到对应的极小元)。所以这样分下去可以得到A1|A2|A3|...|Ap,对于Aj中的每个元素,总可以从Aj-1中找到某个偏序关系在它下方的元素(2<=j<=p)。这样就得到一个链a1<a2<a3<...<ap。由于r是链的最大长度,所以p<=r,又因为x可以被划分为p个反链,所以有r<=p。因此r=p定理得证。(这句话要仔细想想)
(4)所以本题就是找一个最长链,用长度r就是答案。
(5)但是题目可以有环,因为偏序关系是“不小于”。所以一开始用强联通缩点,缩点后每个强连通分量的点权就是这个强联通分量的点数。
(6)强联通分量用Tarjan,找最长链就是一个DFS(DP)。
code:
#include <iostream> #include <cstdio> #include <cstring> #include <stack> using namespace std; const int MAXN=111111,MAXM=333333; struct Edge{ int from,to,next; Edge(){} Edge(int f,int t,int n):from(f),to(t),next(n){} }; struct SCC{ Edge edge[MAXM]; int head[MAXN],tot,n,pre[MAXN],lowlink[MAXN],sccno[MAXN],dfs_clock,scc_cnt; stack<int>S; void init(){ memset(head,-1,sizeof head); tot=0; } void add(int f,int t){ edge[tot]=Edge(f,t,head[f]); head[f]=tot++; } void dfs(int u){ pre[u]=lowlink[u]=++dfs_clock; S.push(u); for(int p=head[u];~p;p=edge[p].next){ int v=edge[p].to; if(!pre[v]){ dfs(v); lowlink[u]=min(lowlink[u],lowlink[v]); }else if(!sccno[v]){ lowlink[u]=min(lowlink[u],pre[v]); } } if(lowlink[u]==pre[u]){ scc_cnt++; for(;;){ int x=S.top();S.pop(); sccno[x]=scc_cnt; if(x==u)break; } } } void find_scc(int _n){ n=_n; dfs_clock=scc_cnt=0; memset(sccno,0,sizeof sccno); memset(pre,0,sizeof pre); while(!S.empty()) S.pop(); for(int i=1;i<=n;i++) if(!pre[i])dfs(i); } }ga; struct Graph{ Edge edge[MAXM]; int head[MAXN],tot,dp[MAXN],w[MAXN]; bool vis[MAXN]; void init(){ memset(head,-1,sizeof head); memset(dp,0,sizeof dp); memset(vis,0,sizeof vis); memset(w,0,sizeof w); tot=0; } void add(int f,int t){ edge[tot]=Edge(f,t,head[f]); head[f]=tot++; } int dfs(int u){ if(vis[u]) return dp[u]; vis[u]=1; int ret=0; for(int p=head[u];~p;p=edge[p].next){ int v=edge[p].to; ret=max(ret,dfs(v)); } return dp[u]=ret+w[u]; } }gb; int main() { // freopen("data.in","r",stdin); int N,M,u,v; while(scanf("%d%d",&N,&M)==2){ ga.init(); for(int i=0;i<M;i++){ scanf("%d%d",&u,&v); ga.add(u,v); } ga.find_scc(N); gb.init(); for(int u=1;u<=N;u++){ for(int p=ga.head[u];~p;p=ga.edge[p].next){ int x=ga.sccno[u]; int y=ga.sccno[ga.edge[p].to]; if(x!=y) gb.add(x,y); } } for(int u=1;u<=N;u++){ int x=ga.sccno[u]; gb.w[x]++; } int ans=0; for(int u=1;u<=ga.scc_cnt;u++) ans=max(ans,gb.dfs(u)); printf("%d\n",ans); } return 0; }