算法学习-拓扑排序(思维例题)

一些知识
AOV网
一个无环的有向图称为有向无环图。有向无环图是描述一个工程、计划、生产、系统等流程的有效工具。一个大工程可以分为若干个子工程(活动),活动之间通常有一定的约束,例如先做什么活动、后做什么活动。
用顶点表示活动,用弧表示活动之间的优先关系的有向图,称为顶点表示活动的网,简称AOV网。
若顶点i到顶点j之间存在一条有向路径,称顶点i为顶点j的前驱,顶点j为顶点i的后继,若i,j是图中的弧,则顶点i是顶点j的直接前驱,顶点j是顶点i的直接后继。
AOV网中的弧表示活动之间存在制约关系(先后关系)。

例子就不说了,数据结构课都说过。

拓扑排序
拓扑排序是将AOV网中的顶点排成一个线性序列,该序列必须满足:若从顶点i到顶点j有一条路径,则该序列中顶点i一定在顶点j之前
注意:拓扑排序并不唯一

算法设计
(1)求出各顶点的入度,存入输出in[]中,并将入度为0的顶点入栈S。
(2)如果栈不为空,则重复执行以下操作:
①栈顶元素i出栈,并保存到拓扑序列数组topo[]中;
②顶点i的所有邻接点入度减1,如果减1后入度为0,则入栈。
(3)如果输出的顶点数小于AOV网的顶点数,则说明图中有环,否则输出拓扑序列。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m;
struct edge{
    int to,next,w;
}e[N];
int head[N],in[N],topo[N],cnt;
stack<int>s;
inline void add(int u,int v){
    e[cnt].to=v,e[cnt].next=head[u];
    head[u]=cnt++;
}
inline bool topsort(){
    int k=0;
    for(int i=0;i<n;++i)
        if(!in[i])s.push(i);
    while(!s.empty()){
        int u=s.top();s.pop();
        topo[k++]=u;
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].to;
            if(--in[v]==0)s.push(v);
        }
    }
    if(k<n)return false;
    return true;
}
int main(){
    cin>>n>>m;
    memset(head,-1,sizeof(head));memset(in,0,sizeof(in));
    for(int i=0;i<m;++i){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);in[v]++;
    }
    if(!topsort())cout<<"该有向图有回路"<<endl;
    else{
        for(int i=0;i<n-1;++i)printf("%d ",topo[i]);
        cout<<topo[n-1]<<endl;
    }
}

例题:

题目描述
设G为有n个顶点的带权有向无环图,G中各顶点的编号为1到n,请设计算法,计算图G中1到n之间的最长路径。

输入格式
输入的第一行有两个整数,分别代表图的点数n和边数m。

第2到第(m+1) 行,每行 33 个整数 u, v, w(u<v),代表存在一条从u到v边权为w的边。

输出格式
输出一行一个整数,代表1到n的最长路。

若1与n不连通,请输出-1。

输入样例
2 1
1 2 1

输出样例
1

数据规模
1<=n<=1500,1<=m<=5×10^4,1<=u,v<=n,-10^5<=w<=10^5

分析

其实这道题用bellman_ford或者SPFA就能解答,当然也可以用拓扑排序来做,但是有一个细节需要注意:
题目要求1到n的最大值,但是对一个有向无环图来说,入度为0的点可不只是1这个点,那么我们不可能不处理他,如果不处理他,这些点的邻接点的入度永远不可能减为0,那样就无法处理了。但是直接入栈结果很可能不对!很可能就不是以1为起点的路径了。
处理方法是:把所有除了1之外的入度为0的点先入栈,然后做一系列出栈操作,把所有入度为0的点的邻接点的入度减1,相当于直接把除了1之外的入度为0的点全部处理了,这并不会影响最终结果,只是单纯避免了处理其他点。当然了,处理完这些点以后,这些点的邻接点入度减去1以后很可能造成其他的出现入度为0的情况,依旧要进行处理,那我们就用循环框住就好了。

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int N=1e5;
int n,m;
struct edge{
    int to,next,w;
}e[N];
int head[N],in[N],topo[N],cnt;
int dis[N];
stack<int>s;
inline void add(int u,int v,int w){
    e[cnt].to=v,e[cnt].next=head[u],e[cnt].w=w;
    head[u]=cnt++;
}
inline void topsort(){
    int k=0;
    //废弃掉其它入度为0的点
    for(int i=2;i<=n;++i){
        dis[i]=-INF;
        if(!in[i])s.push(i);
    }
    while(!s.empty()){
        int x=s.top();s.pop();
        for(int i=head[x];~i;i=e[i].next)
            if(!(--in[e[i].to]))s.push(e[i].to);
    }
    s.push(1);
    while(!s.empty()){
        int u=s.top();s.pop();
        topo[k++]=u;
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].to;
            dis[v]=max(dis[v],dis[u]+e[i].w);
            if(!(--in[v]))s.push(v);
        }
    }
    if(dis[n]!=-INF)cout<<dis[n]<<endl;
    else cout<<-1<<endl;
}
int main(){
    cin>>n>>m;
    memset(head,-1,sizeof(head));memset(in,0,sizeof(in));
    for(int i=0;i<m;++i){
        int u,v,w;scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);in[v]++;
    }
    topsort();
}

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

布布要成为最负责的男人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值