ZOJ 3795 Grouping 强联通+偏序集

来源:互联网 发布:犀牛软件分解模型 编辑:程序博客网 时间:2024/06/06 17:21

题目链接: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;}


0 0
原创粉丝点击