好久没有写了呀...
这些代码都是同组的万福同学一步一步教我们打的,向大佬致敬!!!
先来张图
图来了就要读图呀 怎么读图呢 首先要建图!
我们使用邻接链表的存储方式 于是有了以下两个结构体
struct edge{
int end;
int weight;
int Ee;
int El;
edge * next_edge;
};
struct vertex{
int ind;
int Ve;
int Vl;
edge * first_edge;
};
注意!结构体edge要写在vertex前面 否则 你将获得以下报错
因为它找不到啊!!
这里建图的逻辑是:图是由顶点vertex构成的,而点与点之间是通过边edge连接起来的
所以 这张图G的类型为vertex 于是我们在主函数中写上
int main(){
vertex G[100];
return 0;
}
图建好了 我们就开始读图吧!
输入关键信息:图的顶点数n以及边数m
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
7 9 2
8 9 2
等一下!万一是垃圾数据怎么办!!!我们先养好习惯 初始化一下!!
首先初始化顶点
for (int i = 1; i <= n; ++i) {
G[i].ind = 0;
G[i].first_edge = NULL;
}
由于存图暂时用不上顶点的最早和最晚发生时间 我们后面再对Ve和Vl进行初始化
读入顶点和边的信息
int u,v,w;
for (int i = 0; i < m; ++i) {
edge * now = new edge;
cin>>u>>v>>w;
now->end = v;
now->weight = w;
now->next_edge = G[u].first_edge;
G[u].first_edge = now;
}
其中进行了一个头插入(链表的知识 不会的好好反思一下!)
(在我自己写的时候一直有个疑问 这个边只有终点end的信息 那起点存在哪呢 看到这段代码 你懂了吗)
———————————————读图这项简单的小任务完成啦——————————————
记得我们的struct vertex里面有个ind吧 这个是入度 需要我们自己算 接下来很快就要用到入度这个信息了
算入度!!
for (int i = 1; i <= n; ++i) {
edge * p = G[i].first_edge;
while (p){
G[p->end].ind++;
p = p->next_edge;
}
}
我觉得还挺好理解的 都是套路了
用一个结构体指针p来试探:“有没有边?”
“有!”
那么这条边的终点的入度就+1
“下一条边!”
...
算完了 就是这么简单易懂
——————————————即将进入重头戏!!!——————————————————
在好戏开始之前 我们先准备好要用到的工具——栈和队列
int que[100],head = 0,tail = 0;
int sta[100],top = 0;
(栈和队列中存的都是顶点的编号)
关键路径的要点就是 最早 和 最晚
最早:从左往右算 入度先为0 先有最早发生时间
最晚:从右往左算 越是靠近终点 越早算出最晚发生时间
记得我们前面还没有对这几个变量进行初始化 为了防止垃圾数据 我们养成好习惯 初始化一下
for (int i = 1; i <= n; ++i) {
G[i].Ve = 0;
G[i].Vl = INT_MAX;
}
for (int i = 1; i <= n; ++i) {
edge * p = G[i].first_edge;
while (p){
p->Ee = 0;
p->El = INT_MAX;
p = p->next_edge;
}
}
——————————————重头戏开始了!!!————————————————————
找起点
for (int i = 1; i <= n; ++i) {
if (G[i].ind == 0){
que[tail++] = i;
}
}
算出顶点和边的最早发生时间
while (head < tail){
//出队 入栈
int now = que[head++];
sta[top++] = now;
//存入Ee和Ve后 解除顶点与边的束缚
edge * p = G[now].first_edge;
while (p){
//存入Ee和Ve
p->Ee = G[now].Ve;
G[p->end].Ve = max(G[p->end].Ve,G[p->end].Ve+p->weight);
//解除束缚 即入度-1
G[p->end].ind--;
//解除束缚后 判断是否可以入队
if (G[p->end].ind == 0){
que[tail++] = p->end;
}
//下一条边
p = p->next_edge;
}
}
此时 我们看一下此题中的队列和栈的存入情况
没错 是一样的 那我们为什么要多次一举 建立队列了之后还要建立栈呢?带着这个疑问 我们继续
(其实现在 我们已经存好了顶点和边的最早完成时间了 还能明显地看到一次次刷新的过程)
仔细体会下神奇的while语句
算出最晚发生时间的准备步骤
生成关键路径Critical Path
建立一个结构体来保存关键路径的信息
struct path{
int u;
int v;
int w;
};
主函数中
path CriticalPath[100];
找出终点terminus
int terminus = que[tail-1];
此处注意!为什么要tail-1?
记得我们每次入队都要tail++吗 也就是说que[tail]里面是垃圾数据(如果没有初始化的话)真正存了数据的是0~tail-1个坑
算出工程周期T
int terminus = que[tail-1];
int T = G[terminus].Ve;
G[terminus].Vl = T;
算最晚发生时间
while (top){
int now = sta[--top];
edge * p = G[now].first_edge;
while (p){
p->El = G[p->end].Vl - p->weight;
G[now].Vl = min(G[now].Vl,G[p->end].Vl - p->weight);
if (p->Ee == p->El){
path cp;
cp.u = now;
cp.v = p->end;
cp.w = w;
CriticalPath[index++] = cp;
}
p = p->next_edge;
}
}
完整代码
#include <iostream>
using namespace std;
struct edge{
int end;
int weight;
int Ee;
int El;
edge * next_edge;
};
struct vertex{
int ind;
int Ve;
int Vl;
edge * first_edge;
};
struct path{
int u;
int v;
int w;
};
int main(){
vertex G[100];
int n,m;
puts("请输入顶点数n以及边数m:");
cin>>n>>m;
for (int i = 1; i <= n; ++i) {
G[i].ind = 0;
G[i].first_edge = NULL;
}
int u,v,w;
for (int i = 0; i < m; ++i) {
edge * now = new edge;
cin>>u>>v>>w;
now->end = v;
now->weight = w;
now->next_edge = G[u].first_edge;
G[u].first_edge = now;
}
for (int i = 1; i <= n; ++i) {
edge * p = G[i].first_edge;
while (p){
G[p->end].ind++;
p = p->next_edge;
}
}
int que[100],head = 0,tail = 0;
int sta[100],top = 0;
for (int i = 1; i <= n; ++i) {
G[i].Ve = 0;
G[i].Vl = INT_MAX;
}
for (int i = 1; i <= n; ++i) {
edge * p = G[i].first_edge;
while (p){
p->Ee = 0;
p->El = INT_MAX;
p = p->next_edge;
}
}
for (int i = 1; i <= n; ++i) {
if (G[i].ind == 0){
que[tail++] = i;
}
}
while (head < tail){
//出队 入栈
int now = que[head++];
sta[top++] = now;
//存入Ee和Ve后 解除顶点与边的束缚
edge * p = G[now].first_edge;
while (p){
//存入Ee和Ve
p->Ee = G[now].Ve;
G[p->end].Ve = max(G[p->end].Ve,G[now].Ve+p->weight);
//解除束缚 即入度-1
G[p->end].ind--;
//解除束缚后 判断是否可以入队
if (G[p->end].ind == 0){
que[tail++] = p->end;
}
//下一条边
p = p->next_edge;
}
}
path CriticalPath[100];
int index = 0;
int terminus = que[tail-1];
int T = G[terminus].Ve;
G[terminus].Vl = T;
while (top){
int now = sta[--top];
edge * p = G[now].first_edge;
while (p){
p->El = G[p->end].Vl - p->weight;
G[now].Vl = min(G[now].Vl,G[p->end].Vl - p->weight);
if (p->Ee == p->El){
path cp;
cp.u = now;
cp.v = p->end;
cp.w = w;
CriticalPath[index++] = cp;
}
p = p->next_edge;
}
}
for (int i = 0; i < index; ++i) {
cout<<CriticalPath[i].u<<" "<<CriticalPath[i].v<<" "<<CriticalPath[i].w<<endl;
}
return 0;
}
具体理解我后面再来补充!!!
(肝了两个小时 写累了......)