拓撲排序

1、hdu 4324

題目要求假如a到b沒有邊,那麼b到a一定有邊,且兩點之間只可能有一條邊。根據這個要求,我們可以知道,只要圖中存在環,那麼就一定存在3元環。那麼,只要用拓撲排序刪除入度爲0的節點,計算剩下的節點就可以了。

2、hdu 2647

反向建邊,要注意的是,如果a -> b,c -> b,那麼要從a和c中選擇一個最大的。這裏點太多,需要用鄰接表來表示。

3、hdu 4109

跟hdu 2647題相似,不用反向建邊,一個結點的時間,是所有與其相接的邊的頭結點(u - > v,u爲頭,v爲尾)的時間加上它的安全距離的最大值。

4、hdu 1285 3342 2094

模板題

5、hdu 2497

這題是單純的拓撲排序,但是數據量太大,點和邊數最大都有100W,首先需要用鄰接表來存儲。第一次提交時用了下面這段代碼,超時。

void sort()
{
    for(int i = 1;i <= vertex;i++)
    {
        int j = 1;
        while(j <= vertex && indegree[j])
            j++;
        if(j > vertex)
        {
            puts("IMPOSSIBLE");
            return;
        }
        indegree[j]--;
        result[i] = j;
        j = head[j];
        while(j != -1)
        {
            indegree[adjlist[j].v]--;
            j = adjlist[j].next;
        }
    }
    for(int i = 1;i <= vertex;i++)
        printf("%d\n",result[i]);
}
外層循環一定是vertex(點的個數)次,再看內層循環,找入度爲0的點,每次都需要遍歷indegree[]數組,只要數據強一點,那麼幾乎需要vertex * vertex的時間。所以,這裏可以用隊列優化一下。將入度爲0的點入隊,那麼找入度爲0的點的操作就可以在O(1)內完成。

void sort()
{
    while(!Q.empty())
        Q.pop();
    for(int i = 1;i <= vertex;i++)
        if(!indegree[i])
            Q.push(i);
    for(int i = 1;i <= vertex;i++)
    {
        int j;
        if(Q.empty())
        {
            puts("IMPOSSIBLE");
            return;
        }
        j = Q.front();
        Q.pop();
        indegree[j]--;
        result[i] = j;
        j = head[j];
        while(j != -1)
        {
            indegree[adjlist[j].v]--;
            if(!indegree[adjlist[j].v])
                Q.push(adjlist[j].v);
            j = adjlist[j].next;
        }
    }
    for(int i = 1;i <= vertex;i++)
        printf("%d\n",result[i]);
}
另外,求拓撲排序還有深搜的方法。《算法導論》上有段僞代碼,寫明瞭對於有向無環圖求拓撲排序的方法:每次深搜一個未訪問的結點,點出棧順序的逆順就是我們所求的拓撲排序。但現在的問題是,我們的圖可能是有環的,那麼,用深搜怎麼判斷有環?其實,《算法導論》也給我們指明瞭方法:在深搜時,我們可以用訪問的結點進行染色:初始時,所有結點都是白色的,在搜索一個點時,首先將這個點染成灰色,然後在出棧(即這個點DFS結束時)時將這個點染成黑色。按這種方法,如果當前的點跟接下來要訪問的點的顏色相同,那麼就說明是有環的。用深搜的話,每個點只訪問一次,每條邊也只訪問一次,所以時間複雜度爲O(v + e)。

int head[N],result[N],indegree[N],visited[N];
queue<int> Q;
struct Node
{
    int next;
    int v;
    Node(){};
    Node(int a,int b):next(a),v(b){}
}adjlist[N];

struct Graph_Topology
{
    int vertex,edge,num;
    bool circle;
    int flag;
    void init(int n)
    {
        vertex = n,edge = 0;
        memset(indegree,0,sizeof(indegree));
        memset(visited,0,sizeof(visited));
        for(int i = 0;i <= vertex;i++)
            head[i] = -1;
        num = 0;
        circle = false;
    }

    void insert(int u,int v)
    {
        /*
        int i;
        for(i = head[u];i != -1;i = adjlist[i].next)
            if(adjlist[i].v == v)
                break;
        if(i != -1)
            return;
            */
        adjlist[edge] = Node(head[u],v);
        head[u] = edge++;
        indegree[v]++;
    }

    void sort()
    {
        while(!Q.empty())
            Q.pop();
        for(int i = 1;i <= vertex;i++)
            if(!indegree[i])
                Q.push(i);
        for(int i = 1;i <= vertex;i++)
        {
            int j;
            if(Q.empty())
            {
                puts("IMPOSSIBLE");
                return;
            }
            j = Q.front();
            Q.pop();
            indegree[j]--;
            result[i] = j;
            j = head[j];
            while(j != -1)
            {
                indegree[adjlist[j].v]--;
                if(!indegree[adjlist[j].v])
                    Q.push(adjlist[j].v);
                j = adjlist[j].next;
            }
        }
        for(int i = 1;i <= vertex;i++)
            printf("%d\n",result[i]);
    }

    void DFS()
    {
        for(int i = 1;i <= vertex;i++)
            if(!visited[i])
            {
                //flag = i;
                DFS_Visit(i);
                if(circle)
                {
                    puts("IMPOSSIBLE");
                    return;
                }
            }
        for(int i = num;i >= 1;i--)
            printf("%d\n",result[i]);
    }

    void DFS_Visit(int x)
    {
        if(circle)
            return;
        visited[x] = GREY;
        for(int i = head[x];i != -1;i = adjlist[i].next)
            if(!visited[adjlist[i].v])
                DFS_Visit(adjlist[i].v);
            else if(visited[adjlist[i].v] == visited[x])
            {
                circle = true;
                return;
            }
        visited[x] = BLACK;
        result[++num] = x;
    }
};
6、hdu 1811

對相等的邊合併成一個點,並用這個點進行拓撲排序的計算。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章