題目描述
圖的連接邊上的數據表示其權值,帶權值的圖稱作網。
圖可描述爲頂點集爲(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;
}