算法筆記---問題 A: 關鍵路徑

題目描述

圖的連接邊上的數據表示其權值,帶權值的圖稱作網。
在這裏插入圖片描述
圖可描述爲頂點集爲(a,b,c,d,e)

邊集及其權值爲(始點,終點 權值):

a b 3
a c 2 
b d 5
c d 7
c e 4
d e 6    

網的源點是入度爲0的頂點,匯點是出度爲0的頂點。網的關鍵路徑是指從源點到匯點的所有路徑中,具有最大路徑長度的路徑。上圖中的關鍵路徑爲a->c->d->e,其權值之和爲關鍵路徑的長度爲15。

本題的要求是根據給出的網的鄰接矩陣求該網的關鍵路徑及其長度。

輸入:

第一行輸入一個正整數n(1<=n<=5),其代表測試數據數目,即圖的數目
第二行輸入x(1<=x<=15)代表頂點個數,y(1<=y<=19)代表邊的條數
第三行給出圖中的頂點集,共x個小寫字母表示頂點
接下來每行給出一條邊的始點和終點及其權值,用空格相隔,每行代表一條邊。

輸出:

第一個輸出是圖的關鍵路徑(用給出的字母表示頂點, 用括號將邊括起來,頂點逗號相隔)
第二個輸出是關鍵路徑的長度
每個矩陣對應上面兩個輸出,兩個輸出在同一行用空格間隔,每個矩陣的輸出佔一行。

樣例:
輸入:
2
5 6
abcde
a b 3
a c 2
b d 5
c d 7
c e 4
d e 6
4 5
abcd
a b 2
a c 3
a d 4
b d 1
c d 3
輸出:
(a,c) (c,d) (d,e) 15
(a,c) (c,d) 6

解題思路:

求關鍵路徑的題一般都是有向無環圖,且都是AOE(Activity On Edge)網
即圖中的頂點表示事件,圖中的邊表示活動。
關鍵路徑共有四部分需要求出來:
1、事件的最早發生時間
2、事件的最晚發生時間
3、活動的最早開始時間
4、活動的最晚開始時間

1、事件最早發生的時間,是從前往後順序計算,取最大值
即ve[v] = max{ve[top] + weight}

2、求出事件的最遲發生時間,是從後往前順序計算,取最小值
vl[top] = min{vl[v] - weight}

3、活動的最早開始時間 = 事件的最早發生時間

4、活動的最晚開始時間 = 事件的最晚發生時間 - 該活動所需時間

5、判斷活動的最早開始時間和最晚開始時間是否一樣,兩個時間相同則表示該活動爲關鍵路徑上的活動。

下圖爲王道考研2020的知識點:
關鍵路徑

注意:

該題在輸出關鍵路徑的時候,需要找出圖中的源點,不然可能不會通過測試數據。

下面爲AC代碼:

/*
 * @Description: 關鍵路徑
 */
#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include<string>
using namespace std;
struct path_node
{
    int v; //表示弧頭結點 即 a -> b 此時 v 表示 b;
    int weight;
    path_node(int v, int weight)
    {
        this->v = v;
        this->weight = weight;
    }
};

const int path_max_num = 20; //最大邊數或者結點數
int n, x, y;                 //n表示圖的數目,x表示結點數,y表示邊的數目
vector<path_node> G[path_max_num];
int in_depth[path_max_num];             //入度數組
string vertex;                          //表示結點集合
int ve[path_max_num], vl[path_max_num]; //事件最早發生時間,事件最晚發生時間
stack<int> top_order;//表示拓撲序列
vector<int> path[path_max_num];//關鍵路徑

/**
 * @description: 找出 ch 在 vertex 中的位置
 * @param : 當前結點的值
 * @return: ch 的位置
 */
int find_id_in_vertix(char ch)
{
    return vertex.find(ch);
}

//拓撲排序
bool topology_sort()
{
    fill(ve, ve + path_max_num, 0);
    fill(in_depth,in_depth + path_max_num,0);
    queue<int> q;
    //求出各個結點的入度
    for (int i = 0; i < x; i++)
    {
        for (int j = 0; j < G[i].size(); j++)
        {
            path_node node = G[i][j];
            in_depth[node.v]++;
        }
    }

    for (int i = 0; i < x; i++)
    {
        if (in_depth[i] == 0)
        {
            q.push(i);
        }
    }
    
    while (!q.empty())
    {
        int top = q.front();
        q.pop();
        top_order.push(top);
        for (int i = 0; i < G[top].size(); i++)
        {
            path_node node = G[top][i];
            int v = node.v;
            int weight = node.weight;
            in_depth[v]--;
            if (in_depth[v] == 0)
            {
                q.push(v);
            }
            //求出事件最早發生的時間,此時取最大值,從前往後順序計算
            //ve[v] = max{ve[top] + weight}
            if (ve[top] + weight > ve[v])
            {
                ve[v] = ve[top] + weight;
            }
        }
    }
    if (top_order.size() != x)
    {
        return false; //表示當前的圖不是有向無環圖
    }
    return true;
}

//求出關鍵路徑
int critical_path()
{
    if (topology_sort() == false)
    {
        //表示沒有拓撲序列
        return -1;
    }
    //初始化 vl 數組
    int max_length = 0;
    for(int i = 0;i < x;i++){
        if(ve[i] > max_length){
            max_length = ve[i];
        }
    }
    fill(vl, vl + path_max_num, max_length);
    //直接使用top_order求出事件的最遲發生時間
    while (!top_order.empty())
    {
        int top = top_order.top();
        top_order.pop();
        for (int i = 0; i < G[top].size(); i++)
        {
            path_node node = G[top][i];
            int v = node.v;
            int weight = node.weight;
            //求出事件的最遲發生時間,此時取最小值,從後往前順序計算
            //vl[top] = min{vl[v] - weight}
            if (vl[v] - weight < vl[top])
            {
                vl[top] = vl[v] - weight;
            }
        }
    }

    for(int i = 0;i < x;i++){
        path[i].clear();
    }
    //遍歷鄰接表的所有邊
    //計算活動的最早開始時間和最晚開始時間
    for (int i = 0; i < x; i++)
    {
        for (int j = 0; j < G[i].size(); j++)
        {
            path_node node = G[i][j];
            int v = node.v;
            int weight = node.weight;
            //活動的最早開始時間 e
            //活動的最晚開始時間 l
            int e = ve[i];          //活動的最早開始時間 = 事件的最早發生時間
            int l = vl[v] - weight; // 活動的最晚開始時間 = 事件的最晚發生時間 - 該活動所需時間
            //如果活動的最早開始時間等於活動的最晚開始時間
            if (e == l)
            {
                //表示當前結點爲關鍵路徑的結點
                //printf("(%c,%c) ", vertex[i], vertex[v]);
                //cout << "(" << vertex[i] << "," << vertex[v] << ")" << " ";
                path[i].push_back(v);
            }
        }
    }
    //求出源點
    int s;
    for(int i = 0;i < x;i++){
        if(ve[i] == 0){
            s = i;
            break;
        }
    }
    while(path[s].size()){
        printf("(%c,%c) ",vertex[s],vertex[path[s][0]]);
        s = path[s][0];
    }
    
    return max_length;
}

int main()
{
    cin >> n; //表示共有幾個網絡
    char u_ch, v_ch; //表示起始結點和終止結點 u_ch -> v_ch
    int weight;      //邊的權值
    while(n--){
        cin >> x >> y; //頂點的個數爲x,邊數爲y
        cin >> vertex; //表示結點的集合
        for (int i = 0; i < x; i++)
        {
            G[i].clear(); //初始化圖
        }

        for (int i = 0; i < y; i++)
        {
            cin >> u_ch >> v_ch >> weight;
            int u = find_id_in_vertix(u_ch);
            int v = find_id_in_vertix(v_ch);
            G[u].push_back(path_node(v, weight));
        }
        int path_length = critical_path();
        cout << path_length << endl;
    }
    //system("pause");
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章