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

轉載請註明出處

發佈了45 篇原創文章 · 獲贊 5 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章