拓扑排序 学习笔记

 

拓扑排序适用于DAG(有向无环图)。它可以按照一定的遍历顺序将点重新排列,能减少算法的复杂度。

先要记录点的入度,将入度为0的点入列,然后与它相邻的点in[]--。如果已经为0,入列,以此类推。在遍历的同时将点按照遍历的顺序记录下来即可。

代码:

 while(!q.empty())
    {
        int now=q.front();q.pop();
        pos[++cnt]=now;
        for (int i=head[now];i;i=edge[i].next)
        {
            int to=edge[i].to;
            in[to]--;
            if (!in[to]) q.push(to);
        }
    }

下面是几道拓扑排序的题。

 

T1 小叶子的故事之写代码

 

题目大意:有N个数。给M个关系(x,y),表示x必须在y之前。输出字典序最小的数列。

---------------------------------------------------------------------------------

拓扑排序裸题,只要用小根堆维护点的顺序即可。

#include<bits/stdc++.h>
using namespace std;
int in[100005],ans[100005],vis[100005];
int n,m,u,to,jishu;
priority_queue<int,vector<int>,greater<int> > q;
int cmp(int x,int y)
{
    return x>y;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
vector<int> v[100005];
int main()
{
    n=read(),m=read();
    jishu=0;
    for (int i=1;i<=m;i++)
    {
        u=read(),to=read();
        v[u].push_back(to);in[to]++;
    }
    for (int i=1;i<=n;i++) if (in[i]==0) q.push(i);
    while(!q.empty())
    {
        int now=q.top();q.pop();
        ans[++jishu]=now;
        for (int i=0;i<v[now].size();i++)
        {
            int to=v[now][i];
            in[to]--;
            if (in[to]==0&&vis[to]==0) q.push(to),vis[to]=1;
        }
    }
    if (jishu==n) for (int i=1;i<=jishu;i++) printf("%d ",ans[i]);
    else printf("OMG.");
    return 0;
} 

T2 神经网络

题目链接

----------------------------------

拓扑排序裸题,只不过要判定图是否有环,只要在遍历的时候看看是否有最后不为0的in[]即可。

#include<bits/stdc++.h>
using namespace std;
int n,p,u,v,d,head[500005],cnt,c[500005],U,st[500005],top,indgr[500005];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }while('0'<=ch&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
struct node
{
    int from,to,dis,next;
}edge[500005];
void add(int from,int to,int dis)
{
    edge[++cnt].next=head[from];
    edge[cnt].from=from;
    edge[cnt].to=to;
    edge[cnt].dis=dis;
    head[from]=cnt;
}
void topu()
{
    while(top!=0)
    {
        int now=st[top--];
        if (c[now]<=0){
            for (int i=head[now];i;i=edge[i].next)
            {
                int to=edge[i].to;
                indgr[to]--;
                if (indgr[to]==0) st[++top]=to;
            }
            continue;
        }
        for (int i=head[now];i;i=edge[i].next)
        {
            int to=edge[i].to;
            c[to]+=c[now]*edge[i].dis;
            indgr[to]--;
            if (indgr[to]==0) st[++top]=to;
        }
    }
}
int main()
{
    n=read(),p=read();
    for (int i=1;i<=n;i++)
    {
        c[i]=read(),U=read();
        if (c[i]!=0) st[++top]=i;
        else c[i]-=U;
    }
    for (int i=1;i<=p;i++)
    {
        u=read(),v=read(),d=read();
        add(u,v,d);
        indgr[v]++;
    }
    topu();
    bool flag=0;
    for (int i=1;i<=n;i++)
    {
        if (!head[i]&&c[i]>0){
            cout<<i<<" "<<c[i]<<endl;
            flag=1;
        }
    }
    if (flag==0) cout<<"NULL"<<endl;
    return 0;
 } 

T3 Fox And Names

题目链接

--------------------------------------------

披着字符串外衣的拓扑排序。注意转换大小写。其实就是神经网络的加强版。

#include<bits/stdc++.h>
using namespace std;
vector<int> v[30];
int pos[27],n,in[27],sum;
string a,b;
int main()
{
    scanf("%d",&n);
    if (n==1){
        for (char i='a';i<='z';i++) cout<<i;
        return 0;
    }
    cin>>b;
    for (int j=1;j<n;j++)
    {
        a=b; 
        cin>>b;
        for (int i=0;i<a.length();i++)
        {
            if (i>=b.length()){
                cout<<"Impossible";
                return 0;
            }
            if (a[i]!=b[i]){
                v[a[i]-'a'].push_back(b[i]-'a');
                in[b[i]-'a']++;
                break;
            }
        }
    }
    queue<int> q;
    for (int i=0;i<26;i++) if (in[i]==0) q.push(i);
    while(!q.empty())
    {
        int now=q.front();q.pop();
        sum++;pos[sum]=now;
        for (int i=0;i<v[now].size();i++)
        {
            in[v[now][i]]--;
            if (in[v[now][i]]==0) q.push(v[now][i]);    
        }    
    }
    if (sum!=26){
        cout<<"Impossible";
        return 0;
    }
    for (int i=1;i<=26;i++) cout<<(char)(pos[i]+'a');
    return 0;
}

T4 不相交路径

这种题属于拓扑排序里比较难的题了,综合性较强,如果没有做题经验会很头疼。

题目描述+题解

------------------------------------

后记:拓扑排序和DP结合起来也是一类大问题,比如T4。实际上很多DP题目都可以转化成拓扑排序来做,利用题目给的“优先条件”。至于能否转化,要看各位OIer们的功底了。如果有时间,我会专门写一篇关于DP的随笔,包括这类图上DP的题(也许得要到暑假了,现在做题还是太少。

posted @ 2020-03-13 23:50  我亦如此向往  阅读(255)  评论(0编辑  收藏  举报