sort题解

题目描述

有N个数A1..AN,已知一些它们之间的大小关系,形如某个数不小于某个数。

Your Task

   把这N个数分成尽量少个集合,使得每个集合内的任意两个数的大小关系都是未知的。

输入文件

第一行 N M 表示有N个数,M个大小关系。

接下来M行,每行 i j 表示Ai>=Aj。

输出文件

   一行包含一个整数,最少要分成多少个集合。

样例输入

4 4

    1 2

1 3

1 4

4 1

样例输出

3

样例解释

2 3在同一个集合,1在一个集合,4在一个集合。

数据约定

20%:N<=18

60%:N<=10000

100%:N<=100000,M<=1000000

[题解]

很明显,题目中两个数间的大小关系是种偏序关系,这道题目也就是要求一个偏序集的最小反链覆盖.

        由偏序集的相关知识我们可以知道,最小反链覆盖数等于最长链,所以这道题就是要求出一个有向图的最长链.

        说到这里算法就出来了.由于有环的存在,对这道题,咱们先缩点,然后DFS一遍即可求出最长链(因为环上的点一定相等,所以都可以搞到最长链中).

         考场上用的是YY的算法,复杂度铁定要跪.可怜我搞了这么久oi还不会tarjan= =.今天算是第一个tarjan,看起来还蛮简单的,特别是他一次退栈可以把一个强连通分量全搞出来,快也就是快在这里吧.由于是改的考场代码,所以奇丑无比= =.

Code

program main;
type int=longint;
var
        i,j,k,m,n:int;
        s,h,a,low,dfn,b,tail,dis,s_s:array[1..100000]of int;
        ne,t:array[1..1000000]of int;
        x,y,tot,high,ans:int;

function min(x,y:int):int;
begin
        if x<y then exit(x) else exit(y);
end;

procedure dfs(x:int);var i:int;
begin
        j:=h[x];b[x]:=10009;
        inc(high);a[high]:=x;
        dfn[x]:=high;
        low[x]:=dfn[x];
        while j<>0 do begin
                i:=t[j];
                if b[i]=0 then begin
                        dfs(i);
                        low[x]:=min(low[x],low[i]);
                end else if(b[i]=10009)then begin
                        low[x]:=min(low[x],dfn[i]);
                end;
                j:=ne[j];
        end;
        if low[x]=dfn[x]then begin
                for i:=dfn[x]to high do begin
                        s[a[i]]:=x;b[a[i]]:=8;a[i]:=0;
                end;
                high:=dfn[x]-1;
        end;
end;

procedure deal(x:int);var i,j:int;
begin
        b[x]:=18;
        j:=h[x];
        while j<>0 do begin
                i:=t[j];
                if b[i]<>18 then deal(i);
                if dis[i]>dis[x]then dis[x]:=dis[i];
                j:=ne[j];
        end;
        inc(dis[x],s_s[x]+1);
        if dis[x]>ans then ans:=dis[x];
end;

begin
        assign(input,'sort.in');reset(input);
        assign(output,'sort.out');rewrite(output);
        read(n,m);
        for i:=1 to n do s[i]:=i;
        for i:=1 to m do begin
                read(x,y);inc(tot);
                if h[x]=0 then tail[x]:=tot;
                t[tot]:=y;ne[tot]:=h[x];h[x]:=tot;
        end;
        for i:=1 to n do if b[i]=0 then dfs(i);
        for i:=1 to n do begin
                if s[i]=i then continue;
                ne[tail[s[i]]]:=h[i];h[i]:=0;tail[s[i]]:=tail[i];
                inc(s_s[s[i]]);
        end;
        for i:=1 to m do t[i]:=s[t[i]];
        ans:=0;
        for i:=1 to n do if(b[i]<>18)and(s[i]=i)then deal(i);
        write(ans);
        close(input);close(output);
end.


写tarjan时犯了2个nc的错误:

(1)dfs时b数组在退栈前就清掉了

(2)缩点时没有改si的tail指针

附上另一个人的blog地址: http://blog.csdn.net/jerrydung/article/details/8039545

他的代码是C++的.


BY QW

转载请注明出处

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值