算法之拓扑排序

  拓扑排序就是把有向无环图变成一种线性的序列,这种线性序列有前后关系。如果用队列来存储最后的结果,如果队列中的数据与总的数据n不相等,则说明有环。

  此拓扑排序的思想是:

  (1)从有向图中选取一个没有前驱的顶点,并输出之;

  (2)从有向图中删去此顶点以及所有以它为尾的弧;

  (3)如果入度没有为0的表示有环,这时候如果还是向遍历,可以自己加一个起点,然后连接到任意一个图节点即可。

  拓扑排序简易代码:

  

queue<int>q;
//priority_queue<int,vector<int>,greater<int>>q;
//优先队列的话,会按照数值大小有顺序的输出
//此处为了理解,暂时就用简单队列
TopoSort()
{
    for(inti=1;i<=n;i++)
    {
        if(indegree[i]==0)
        {
            q.push(i);
        }
    }
 
    inttemp;
    while(!q.empty())
    {
        temp=q.front();//如果是优先队列,这里可以是top()
        printf("%d->",temp);
        q.pop();
        for(inti=1;i<=n;i++)//遍历从temp出发的每一条边,入度--
        {
            if(map[temp][i])
            {
                indegree[i]--;
                if(indegree[i]==0)q.push(i);
            }
        }
    }
}

  hdu今天竟然关闭了,只能自己先随便写写了:

使用了数组存储图的信息:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<iterator>
using namespace std;
int Map[100][100];
int indegree[100];
queue<int> resq;
void TopoSort(int n)
{
    queue<int> temq;
    while(!resq.empty())
    {
        resq.pop();
    }
    for(int i=0;i<n;++i)
    {
        if(indegree[i]==0)
        {
            temq.push(i);
        }
    }

    while(!temq.empty())
    {
        int temp = temq.front();
        temq.pop();
        ++cnt;
        resq.push(temp);
        for(int i=0;i<n;++i)
        {
            if(Map[temp][i])
            {
                --indegree[i];
                if(indegree[i]==0)
                {
                    temq.push(i);
                }
            }
        }
    }

}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        memset(Map,0,sizeof(Map));
        memset(indegree,0,sizeof(indegree));
        for(int i=0;i<m;++i)
        {
            int a1,a2;
            scanf("%d%d",&a1,&a2);
            if(Map[a1][a2]==0)
            {
                Map[a1][a2] = 1;
                ++indegree[a2];
            }
        }
        printf("answer:
");
        TopoSort(n);
        while(!resq.empty())
        {
            int a = resq.front();
            resq.pop();
            printf("%d ",a);
        }
        printf("
");
    }
    return 0;
}
/*
5 4
0 1
1 2
1 4
3 1
*/
View Code

 hdu1285,它拓扑的时候还要从小到大排序。有一个输入也是坑,有重复数据。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
int Map[501][501];
int indegree[501];
queue<int> qi;

void TopoSort(int n)
{
    //优先队列
    priority_queue<int,vector<int>,greater<int> > q;
    for(int i=1;i<=n;++i)
    {
        if(indegree[i]==0)
            q.push(i);
    }
    while(!q.empty())
    {
       int top = q.top();
       q.pop();
       qi.push(top);
       for(int i=1;i<=n;++i)
         if(Map[top][i])
        {
            --indegree[i];
            if(indegree[i]==0)
                q.push(i);
        }
    }
}

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        memset(Map,0,sizeof(Map));
        memset(indegree,0,sizeof(indegree));
        for(int i=0;i<m;++i)
        {
            int win,lose;
            scanf("%d%d",&win,&lose);
            //不加会报错,测试数据会有重复!!!
            if(Map[win][lose]==0)
            {
                Map[win][lose] = 1;
                ++indegree[lose];
            }
            
        }
        TopoSort(n);
        while(!qi.empty())
        {
            printf("%d",qi.front());
            qi.pop();
            if(!qi.empty())
                printf(" ");
        }
        printf("
");
    }
    return 0;
}
View Code

hdu2647

此题的map不能使用数组,不然内存会过不去,使用vector。拓扑顺序跟输入相反。

这次测试数据没有重复数据,并且需要判断是否有环。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<map>
using namespace std;

vector<int> Map[10001];
int indegree[10001];
int tcost;
map<int,int> mii;

void init()
{
    for(int i=0;i<=10000;++i)
        Map[i].clear();
    memset(indegree,0,sizeof(indegree));
    tcost = 0;
    mii.clear();
}

void TopoSort(int n)
{
    //暂时,结果队列
    queue<int> qt,qr;
    int len,nextlen,price;
    len = nextlen = 0;
    price = 888;
    for(int i=1;i<=n;++i)
        if(indegree[i]==0)
        {
            qt.push(i);
            tcost += price;
            ++len;
        }
    ++price;
    while(!qt.empty())
    {
        int top = qt.front();
        qt.pop();
        qr.push(top);
        --len;
        for(int i=0;i<Map[top].size();++i)
        {
            --indegree[Map[top][i]];
            if(indegree[Map[top][i]]==0)
            {
                ++nextlen;
                tcost += price;
                qt.push(Map[top][i]);
            }
        }
        if(len == 0)
        {
            len = nextlen;
            nextlen = 0;
            ++price;
        }
    }
    if(qr.size()!=n)
    {
        tcost = -1;
    }
}

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(int i=0;i<m;++i)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            //反向
            pair<map<int,int>::iterator,bool> mib = mii.insert(pair<int,int>(a+10000*b,a));
            //去重复数据,虽然测试数据没有重复数据
            if(mib.second)
            {
                ++indegree[a];
                Map[b].push_back(a);
            }
        }
        TopoSort(n);
        printf("%d
",tcost);
    }
    return 0;
}
View Code

hdu3342,标准拓扑排序题,很简单,判断是否有环就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

int Map[101][101];
int indegree[101];

void TopoSort(int n)
{
    queue<int> qtemp,qres;
    for(int i=0;i<n;++i)
        if(indegree[i]==0)
            qtemp.push(i);
    while(!qtemp.empty())
    {
        int top = qtemp.front();
        qtemp.pop();
        qres.push(top);
        for(int i=0;i<n;++i)
            if(Map[top][i])
            {
                --indegree[i];
                if(indegree[i]==0)
                    qtemp.push(i);
            }
    }
    if(qres.size()==n)
        printf("YES
");
    else    printf("NO
");
}

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m),n)
    {
        memset(Map,0,sizeof(Map));
        memset(indegree,0,sizeof(indegree));
        for(int i=0;i<m;++i)
        {
            int mas,pre;
            scanf("%d%d",&mas,&pre);
            if(Map[mas][pre]==0)
            {
                Map[mas][pre] = 1;
                ++indegree[pre];
            }
        }
        TopoSort(n);
    }
    return 0;
}
View Code

hdu4857,这道题目比较坑的是一个数据:

1

3 1

3 1

答案是3 1 2,而我输出2 3 1。

因此需要逆序拓扑。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<map>
using namespace std;

vector<int> vi[30001];
int indegree[30001];
map<int,int> mii;

void init(int n)
{
    for(int i=0;i<=n;++i)
        vi[i].clear();
    memset(indegree,0,sizeof(indegree));
    mii.clear();
}

void TopoSort(int n)
{
    priority_queue<int,vector<int>,less<int> > qtemp;
    stack<int> si;
    int cnt = 0;
    for(int i=1;i<=n;++i)
        if(indegree[i]==0)
        {
            qtemp.push(i);
        }
    while(!qtemp.empty())
    {
        int top = qtemp.top();
        qtemp.pop();
        si.push(top);
        int iSize = vi[top].size();
        for(int i=0;i<iSize;++i)
        {
            int data = vi[top][i];
            --indegree[data];
            if(indegree[data]==0)
                qtemp.push(data);
        }
    }
    while(!si.empty())
    {
        printf("%d",si.top());
        si.pop();
        if(!si.empty()) printf(" ");
    }
    printf("
");
}
int main()
{
    int t,n,m;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init(n);
        for(int i=0;i<m;++i)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            pair<map<int,int>::iterator,bool> pmb = mii.insert(pair<int,int>(a*30000+b,a));
            if(pmb.second)
            {
                vi[b].push_back(a);
                ++indegree[a];
            }
        }
        TopoSort(n);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/jlyg/p/7233027.html