AOE关键路径

AOE网上的关键路径

Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^

题目描述

    一个无环的有向图称为无环图(Directed Acyclic Graph),简称DAG图。 
   
 AOE(Activity On Edge)网:顾名思义,用边表示活动的网,当然它也是DAG。与AOV不同,活动都表示在了边上,如下图所示:
                                     

    
如上所示,共有11项活动(11条边),9个事件(9个顶点)。整个工程只有一个开始点和一个完成点。即只有一个入度为零的点(源点)和只有一个出度为零的点(汇点)。
    
关键路径:是从开始点到完成点的最长路径的长度。路径的长度是边上活动耗费的时间。如上图所示,到 579是关键路径(关键路径不止一条,请输出字典序最小的),权值的和为18

输入

    这里有多组数据,保证不超过10组,保证只有一个源点和汇点。输入一个顶点数n(2<=n<=10000),边数m(1<=m <=50000),接下来m行,输入起点sv,终点ev,权值w1<=sv,ev<=n,sv != ev,1<=w <=20)。数据保证图连通。

输出

    关键路径的权值和,并且从源点输出关键路径上的路径(如果有多条,请输出字典序最小的)。

示例输入

9 11
1 2 6
1 3 4
1 4 5
2 5 1
3 5 1
4 6 2
5 7 9
5 8 7
6 8 4
8 9 4
7 9 2

示例输出

18
1 2
2 5
5 7
7 9


/*
    边-->活动  点-->事件
*/
# include <bits/stdc++.h>
# define MAXN 10010 //最大顶点数
# define MAXM 50010 //最大边数
using namespace std;
struct node
{
    int d; //<u,v>之间的距离
    int to; //v点
    int on;//活动序号
    struct node*next;
};
typedef struct node Node;
struct node *ls1[MAXN]; //正向存储图的信息,出边表的表头
struct node *ls2[MAXN]; //逆向存储图的信息,入边表的表头
int Ee[MAXN]; //各事件最早可能开始的时间
int El[MAXN];//各事件的最迟允许开始时间
int e[MAXN]; //各活动最早可能开始的时间等于Ee
int l[MAXN]; //各活动的最迟允许开始时间
int cnt1[MAXN];//统计正向的各个点的入度
int cnt2[MAXN];//统计反向的各个点的出度
int n,m; //n 顶点数 m 边数
void O_TopSort() //正向拓扑求Ee,可以认为是单点求最长路径
{
    int i,u,v;
    Node*p;
    memset(Ee,0,sizeof(Ee));
    stack<int>s;
    for(i=1;i<=n;i++)
    {
        if(cnt1[i] == 0)//入度为0的点入栈
            s.push(i);
    }
    while(!s.empty())
    {
        u = s.top();
        s.pop();
        p = ls1[u]; //找到从u发出的每条边,将终点v度数减去1
        while(p)
        {
            v = p->to;
            cnt1[v]--;
            if(cnt1[v] == 0)
                s.push(v);
            if(Ee[u] + p->d > Ee[v])
                Ee[v] = Ee[u] + p->d;
            p = p->next;
        }
    }
}
void R_TopSort() //逆序拓扑
{
    int i,u,v;
    Node*p;
    memset(El,0,sizeof(El));
    stack<int> s;
    for(i=1;i<=n;i++)
    {
        El[i] = Ee[n];
        if(cnt2[i] == 0)
            s.push(i);
    }
    while(!s.empty())
    {
        u = s.top();
        s.pop();
        p = ls2[u];
        while(p)
        {
            v = p->to;
            if(--cnt2[v] == 0)
                s.push(v);
            if(El[u] - p->d < El[v])
                El[v] = El[u] - p->d;
            p = p->next;
        }
    }
}

int main()
{
    int i,u,v,w;
    Node*p;
    Node*q;
    while(cin>>n>>m)
    {
        memset(cnt1,0,sizeof(cnt1));
        memset(cnt2,0,sizeof(cnt2));
        for(i=1;i<=n;i++)
        {
            ls1[i] = NULL;
            ls2[i] = NULL;
        }
        for(i=0;i<m;i++)//输入m条边的信息
        {
            cin>>u>>v>>w;
            /*出边表*/
            p = new Node;
            p->d = w;
            p->to = v;
            p->on = i;
            cnt1[v]++;
            p->next = ls1[u];
            ls1[u] = p;
            /*入边表*/
            q = new Node;
            q->d = w;
            q->to = u;
            q->on = i;
            cnt2[u]++;
            q->next = ls2[v];
            ls2[v] = q;
        }
        O_TopSort();//源点到汇点的最长距离
        cout<<Ee[n]<<endl;
        R_TopSort();
        int k,j;
        int Min;
        int flag;
        for(i=1;i<=n;i++)
        {
            p = ls1[i];
            flag = 0;
            if(p)
                Min = p->to;
            while(p)
            {
                j = p->to;
                k = p->on;//取出改点编号
                e[k] = Ee[i]; //边<i,j>
                l[k] = El[j] - p->d;
                if(e[k] == l[k])//为关键活动
                {
                    if(Min > j)
                    {
                        Min = j;
                    }
                    flag = 1;
                }
                p = p->next;
            }
            if(flag)
            {
                cout<<i<<" "<<Min<<endl;
                i = Min - 1;//直接跳转到当前关键路径节点的下一节点
                ls1[i] = NULL;//将字典序最小的赋值
            }
        }
    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值