计算工程完成的关键路径

计算工程完成的关键路径

题目信息

AOE 网络是有向无环加权图,其中顶点表示事件,弧表示活动,权表示活动持续的时间,通常可以用来估算工程完成的时间,即图中从开始点到结束点之间最长的路径对应的时间。请完成一个程序,完成下列任务:
1 、计算 AOE 网络对应的拓扑排序。如果排序结果不唯一,请输出按照从小到大的顺序排列的结果。从小到大的顺序就是输入的节点序列顺序(参见下面关于输入格式的说明)。如图1中满足要求的拓扑排序是: a-b-c-d-e-f-g-h-k ,图2中满足要求的拓扑排序是:v1-v3-v5-v2-v6-v4-v7-v8-v9
2 、计算 AOE 网络的关键路径。注意关键路径可能不唯一,要求输出所有的关键路径。同样,按照是按照从小到大的顺序输出。例,如果得到两条关键路径,分别是0-1-3-6-8-9和0-1-3-4-5-8-9,那么先输出后一条路径,因为两条路径中前三个节点相同,而后一条路径第四个节点的编号小。

在这里插入图片描述
在这里插入图片描述

输入

节点的个数,边的条数;
各个节点的名称序列
边: < 起点 , 终点 , 权值 > 。说明起点和终点是在各个点在输入序列中的位置,如图1中边 <a,b> 表示为 <0,1,6> 。

输出

拓扑排序
关键路径

测试样例

测试样例1

9,11
a,b,c,d,e,f,g,h,k
<0,1,6>,<0,2,4>,<0,3,5>,<1,4,1>,<2,4,1>,<4,6,8>,<4,7,7>,<3,5,2>,<5,7,4>,<6,8,2>,<7,8,4>
a-b-c-d-e-f-g-h-k
a-b-e-h-k

测试样例2

9,11
v1,v2,v3,v4,v5,v6,v7,v8,v9
<0,4,6>,<0,2,4>,<0,5,5>,<4,1,1>,<2,1,1>,<1,6,8>,<1,7,7>,<5,3,2>,<3,7,4>,<6,8,2>,<7,8,4>
v1-v3-v5-v2-v6-v4-v7-v8-v9
v1-v5-v2-v8-v9

测试样例3

4,4
A,B,C,D
<0,1,2>,<1,3,3>,<3,2,5>,<2,1,1>
NO TOPOLOGICAL PATH

解答

#include <iostream>
#include <cstring>
#include <algorithm>
#include <stack>

using namespace std;
char ch;
int NodeNum, EdgeNum;
int topo[55], path[100];
struct Link
{
    
    
    int dir;//目标位置
    int val;//权值
};
struct Node
{
    
    
    char name[5];
    int InNum = 0; //入度
    int OutNum = 0; //出度
    int Etv, Ltv;//存储事件的最早和最晚发生时间
    Link links[300];
    int LinkNum = 0;
} nodes[55];

int cmp1(const void *p1, const void *p2)
{
    
    
    Link *c = (Link *) p1;
    Link *d = (Link *) p2;
    return (c->dir) > (d->dir);
}

struct Rule2
{
    
    
    bool operator()(const int &a, const int &b) const
    {
    
    
        return a > b;
    }
};

void CreateGraph()
{
    
    
    char str[500] = {
    
    '\0'};
    scanf("%s", str);
    int len = strlen(str), q = 0, k = 0;
    for (int i = 0; i < len; i++)
    {
    
    
        if (str[i] != ',')
            nodes[k].name[q++] = str[i];
        else
        {
    
    
            nodes[k].name[q] = '\0';
            k++;
            q = 0;
        }
    }

    int p = 0, k_flag = 0;
    int ch_data[5], cd_p = 0;
    char chs[10] = {
    
    '\0'}, ch;
    getchar();
    while ((ch = getchar()))
    {
    
    
        if (ch == '\n' || ch == -1)
            break;
        if (ch == '<')
        {
    
    
            k_flag = 1;
            continue;
        }
        if (ch == ',' && k_flag == 1)
        {
    
    
            ch_data[cd_p] = atoi(chs);
            memset(chs, '\0', sizeof(chs));
            p = 0;
            cd_p++;
            continue;
        }
        if (ch != '<' && ch != ',' && k_flag == 1 && ch != '>')
        {
    
    
            chs[p++] = ch;
            continue;
        }
        if (ch == '>' && k_flag == 1)
        {
    
    
            ch_data[cd_p] = atoi(chs);
            int u = ch_data[0];
            nodes[u].links[nodes[u].LinkNum].dir = ch_data[1];
            nodes[u].links[nodes[u].LinkNum].val = ch_data[2];
            nodes[u].OutNum++;
            nodes[ch_data[1]].InNum++;
            nodes[u].LinkNum++;
            k_flag = 0;
            p = 0;
            cd_p = 0;
            memset(chs, '\0', sizeof(chs));
            continue;
        }
    }
    for (int i = 0; i < NodeNum; i++) //先对邻节点排序,输出即是按顺序的
        qsort(nodes[i].links, nodes[i].LinkNum + 1, sizeof(nodes[i].links[0]), cmp1);
}

int TopoLogicalSort_DFS()
{
    
    //广度优先搜索获取拓扑序列
    int *Stack;
    Stack = (int *) malloc(sizeof(int) * NodeNum); //分配栈空间
    int top = 0;
    for (int i = 0; i < NodeNum; i++)
    {
    
    
        nodes[i].Etv = 0; //初始化各事件最早发生事件为0
        if (nodes[i].InNum == 0)
        {
    
    //将入度为0的顶点入栈,既找到了开头节点
            Stack[top++] = i;
        }
    }
    int count = 0;
    while (top > 0)
    {
    
    //采用广度优先搜索获取拓扑序列
        int u = Stack[--top];
        topo[count++] = u;

        for (int i = 0; i <= nodes[u].LinkNum; i++)
        {
    
    //将u的邻接点入度减1,并将入度为0的顶点入栈
            int v = nodes[u].links[i].dir;
            if (nodes[v].Etv < nodes[u].Etv + nodes[u].links[i].val)
            {
    
    //更新各顶点事件的最早发生时间
                nodes[v].Etv = nodes[u].Etv + nodes[u].links[i].val;
            }
            if (--nodes[v].InNum == 0)
            {
    
    
                Stack[top++] = v;
            }
        }
        sort(Stack, Stack + top, Rule2());
        //使得输出的拓扑序列中无拓扑关系的节点要从小到大输出,但入栈是从大到小
    }
    return (count == NodeNum);//如果count小于顶点数,说明存在环
}

void PrintPath(int top, int end)
{
    
    
    int u = path[top - 1];
    if (u == end)
    {
    
    
        cout << nodes[path[0]].name; //输出关键路径
        for (int i = 1; i < top; i++)
        {
    
    
            cout<<"-"<<nodes[path[i]].name;
        }
        cout << endl;
        return;
    }
    for (int i = 0; i <= nodes[u].LinkNum; i++)
    {
    
    
        int v = nodes[u].links[i].dir;
        if (nodes[u].Etv + nodes[u].links[i].val < nodes[v].Etv)
            continue;
        if (nodes[v].Etv == nodes[v].Ltv)//关键事件
        {
    
    
            path[top++] = nodes[u].links[i].dir;//入栈
            PrintPath(top, end);
            top--;//退栈
        }
    }
}

void CriticalPath()
{
    
    //求关键路径
    if (!TopoLogicalSort_DFS())
    {
    
    
        cout << "NO TOPOLOGICAL PATH" << endl;
        return;
    }
    for (int i = 0; i < NodeNum; i++)
    {
    
    
        nodes[i].Ltv = nodes[NodeNum - 1].Etv; //初始化各事件最晚发生事件为最后一个事件发生的时间
        if (i)
        {
    
    
            cout << "-";
        }
        cout << nodes[topo[i]].name; //这里方便题目要求的打印拓扑序列
    }
    cout << endl;
    for (int i = NodeNum - 2; i >= 0; i--)
    {
    
    //倒着遍历拓扑排序的点,从倒数第二个开始
        int u = topo[i];
        for (int j = 0; j <= nodes[u].LinkNum; j++)
        {
    
    
            int v = nodes[u].links[j].dir;
            if (nodes[u].Ltv > nodes[v].Ltv - nodes[u].links[j].val)
            {
    
    //更新各顶点事件的最晚发生时间
                nodes[u].Ltv = nodes[v].Ltv - nodes[u].links[j].val;
            }
        }
    }
    path[0] = topo[0];
    PrintPath(1, topo[NodeNum - 1]);//从第一个点到最后一个点
}

int main()
{
    
    
    //freopen("/Users/zhj/Downloads/test.txt", "r", stdin);
    cin >> NodeNum >> ch >> EdgeNum;
    CreateGraph();//把顶点和边信息读入到表示图的邻接表中
    CriticalPath();//求关键路径
    return 0;
}

想法

本题的考察点就在于拓扑排序和关键路径上

之前用另外一种方法构建的子函数有一些问题,但尚未找到问题所在

struct Rule1
{
    
    
    bool operator()(const Link &a, const Link &b) const
    {
    
    
        return a.dir < b.dir;
    }
};

void CreateGraph()
{
    
    //把顶点和边信息读入到表示图的邻接表中
    string str;
    cin >> str;
    int len = str.length();
    int q = 0, k = 0;
    //用q来暂时记录name信息,因为节点名称不止一个字符,用k来记录第几个边
    for (int i = 0; i < len; i++)
    {
    
    
        if (str[i] != ',')
        {
    
    
            nodes[k].name[q++] = str[i];
        }
        else
        {
    
    
            k++;
            q = 0;
        }
    }

    cin >> str;
    len = str.length();

    bool flag = false;
    int Num_Num = 0;//第几个数字
    int Num_Data[3];
    int Char_Num;
    string Char_Data;
    for (int i = 0; i < len; i++)
    {
    
    
        if (str[i] == '<')
        {
    
    //表明是括号对开始
            flag = true;
        }
        else if (str[i] == ',' && flag)
        {
    
    //一个数字已经凑齐了
            Num_Data[Num_Num] = atoi(Char_Data.c_str());
            Char_Data.clear();
            Char_Num = 0;
            Num_Num++;
        }
        else if (str[i] != '<' && str[i] != ',' && str[i] != '>' && flag)
        {
    
    //此时他应该是个数字
            Char_Data[Char_Num++] = str[i];
        }
        else if (str[i] == '>' && flag)
        {
    
    //括号对结束。0起点,1终点,2权值
            Num_Data[Num_Num] = atoi(Char_Data.c_str());
            int tempNum = Num_Data[0];
            nodes[tempNum].links[nodes[tempNum].LinkNum].dir = Num_Data[1];
            nodes[tempNum].links[nodes[tempNum].LinkNum].val = Num_Data[2];
            nodes[tempNum].OutNum++;//对应的出度+1
            nodes[Num_Data[1]].InNum++;//相应的入度+1
            nodes[tempNum].LinkNum++;
            flag = false;
            Num_Num = 0;
            Char_Num = 0;
            Char_Data.clear();
        }
    }

    for (int i = 0; i < NodeNum; i++)
    {
    
    //先对邻节点排序,输出即是按顺序的
        sort(nodes[i].links, nodes[i].links + nodes[i].LinkNum, Rule1());
    }
}

猜你喜欢

转载自blog.csdn.net/zhj12399/article/details/109783893