图最长路径:深大数据结构期末考上机题3
题目是这样的: 有一无向图,用邻接表存储,求从5顶点出发到各个顶点的最长路径长度,输出 最长路径长度 和 最长路径
偷偷把草稿纸带出来的屑
输入:
顶点数
边数
边
样例输入:
8
13
0 2 0.26
1 3 0.29
3 6 0.52
3 7 0.39
4 0 0.38
4 7 0.37
5 1 0.32
5 4 0.35
5 7 0.28
6 0 0.58
6 2 0.4
6 4 0.93
7 2 0.34
样例输出:
5->0 length is 2.44 path is: 5 1 3 6 4 0
5->1 length is 0.32 path is: 5 1
5->2 length is 2.77 path is: 5 1 3 6 4 7 2
5->3 length is 0.61 path is: 5 1 3
5->4 length is 2.06 path is: 5 1 3 6 4
5->6 length is 1.13 path is: 5 1 3 6
5->7 length is 2.43 path is: 5 1 3 6 4 7
(OJ当天就关了我也不知道上面的自制样例是不是真的正确)
当时卡了好一会,后来改出来了,我是用迪杰斯特拉做的,这里迪杰斯特拉要改几条语句,否则路径不对,然后后面问了下大佬,说是关键路径做的
迪杰斯特拉改的关键
关键就是迪杰斯特拉更新节点 i 的时候要重置 i 节点的路径的访问标志数组,也就是 visited[i] = 0 然后需要多更新一次,因为这一次更新的路径(起点到i)必然作为下一次更新时选择的候选路径,图解原因如下:
大致思路
- 将已知的最长的,未被选择过的路径 p_max 的终点 max_index 这个节点,作为更新的点
- 看是否有路径从 max_index 顶点出发,到 j 顶点的路径长度 :length(max_index -> j) + length(起点 -> max_index) 大于已知的到达 j 顶点的路径长度:length(起点 -> j)
即
length(max_index -> j) + length(起点 -> max_index) > length(起点 -> j) ?
访问重置 的解释
- 如果更新成功,新路径长度一定大于我们选择的路径的长度,因为多加了一条边(没有负权边)
- 现在选择的路径已经是已知路径中最长的了,新路径长于现在选择的路径,那么新路径一定是下一次选择最长已知路径中的备选方案,所以visited[]标志要置零
(或者这么理解:假设之前我们有一条A到C点的路径:
[A B C] 叫做 p1,我们认为它是最长的,我们用 p1 更新了A到F,G点的路径,但是现在发现了更长的A到C的路径:
[A D C] 叫做 p2,那么意味着从A到F,G点的路径有了更好的解,所以下一次应该基于 [A D C] 这条路径去更新F,G,但是因为在这之前已经基于 [A D C] 更新过到F,G的路径,visited[C] = 1,所以为了能够再次更新,需要重置标志)
多更新一次 的解释
- visited 标志置零说明总的更新次数要比预计的多选一次,故最外圈循环多一次,即 k = k - 1
更新最长路径的代码:
class edge
{
public:
edge();
int v2; // 边指向的顶点
double weight; // 边权值
};
#define maxlen 1009
deque<edge> adj[maxlen]; // 邻接表,edge 是边的类,包含顶点,权值成员
int visited[maxlen]; // visited[i]:起点到 i 节点的路径是否已经被选择过
double path[maxlen]; // path[i]:起点到 i 节点的最长路径长度
node PATH[maxlen]; // PATH[i]:链表保存起点到 i节点的最长路径
int n; // 顶点
int e; // 边
int i, j, k;
// 迪杰斯特拉 改
for(k=0; k<n; k++)
{
// 选择未被选择过的最长的一条路径
double max = -1145141919;
int max_index = 0;
for(i=0; i<n; i++)
{
if(path[i]>max && visited[i]==0)
{
max = path[i];
max_index = i;
}
}
visited[max_index] = 1;
// 从 max_index 顶点出发更新最长路径
deque<edge>::iterator iter;
for(iter=adj[max_index].begin(); iter!=adj[max_index].end(); iter++)
{
double weight = iter->weight;
int v2 = iter->v2;
if(weight+max > path[v2])
{
path[v2] = weight + max;
PATH[v2] = *(PATH[max_index].cpy());
PATH[v2].insert(v2);
// 关键:下次可选 + 多选一次
visited[v2] = 0;
k -= 1;
}
}
}
完整代码:
#include <iostream>
#include <deque>
#include <cstring>
using namespace std;
// class edge
class edge
{
public:
edge();
int v2; // 边指向的顶点
double weight; // 边权值
};
edge::edge()
{
}
// end of class edge
// class node
class node
{
public:
node();
node* next;
int data;
void insert(int dt);
node* cpy();
void out();
};
node::node()
{
this->next = NULL;
}
// 表尾插入
void node::insert(int dt)
{
node* nd = new node();
nd->data = dt;
node* p = this;
while(p->next)
{
p = p->next;
}
p->next = nd;
}
// 复制一份自己
node* node::cpy()
{
node* nd = new node();
node* p = this->next;
while(p)
{
nd->insert(p->data);
p = p->next;
}
return nd;
}
void node::out()
{
node* p = this->next;
while(p)
{
cout<<p->data<<" ";
p = p->next;
}
}
// end of class node
#define maxlen 1009
deque<edge> adj[maxlen]; // 邻接表
int visited[maxlen]; // 边的访问控制数组
double path[maxlen]; // path[i]:通向 i 节点的最长路径长度
node PATH[maxlen]; // PATH[i]:保存通向i节点的最长路径
int n; // 顶点
int e; // 边
int i, j, k;
int main()
{
cin>>n>>e;
memset(visited, 0, sizeof(visited));
memset(path, (double)0.0, sizeof(path));
// 读取边
for(i=0; i<e; i++)
{
int v1, v2;
double w;
cin>>v1>>v2>>w;
edge* new_e = new edge();
new_e->v2 = v2;
new_e->weight = w;
adj[v1].push_back(*new_e);
}
// 起点的直连路径 初始化
#define start 5
deque<edge>::iterator iter;
for(iter=adj[start].begin(); iter!=adj[start].end(); iter++)
{
int v2 = iter->v2;
double weight = iter->weight;
path[v2] = weight;
PATH[v2].insert(v2);
}
// 迪杰斯特拉 改
for(k=0; k<n; k++)
{
// 选择未被选择过的最长的一条路径
double max = -1145141919;
int max_index = 0;
for(i=0; i<n; i++)
{
if(path[i]>max && visited[i]==0)
{
max = path[i];
max_index = i;
}
}
visited[max_index] = 1;
// cout<<"select "<<max_index<<" "<<max<<endl;
// 从 max_index 顶点出发更新最长路径
deque<edge>::iterator iter;
for(iter=adj[max_index].begin(); iter!=adj[max_index].end(); iter++)
{
double weight = iter->weight;
int v2 = iter->v2;
if(weight+max > path[v2])
{
path[v2] = weight + max;
PATH[v2] = *(PATH[max_index].cpy());
PATH[v2].insert(v2);
// 关键:下次可选 + 多选一次
visited[v2] = 0;
k -= 1;
}
}
}
// 输出
for(i=0; i<n; i++)
{
if(i != start)
{
cout<<start<<"->"<<i<<" length is "<<path[i]<<" path is: "<<start<<" ";
PATH[i].out();
cout<<endl;
}
}
return 0;
}
/*
sample input :
8
13
0 2 0.26
1 3 0.29
3 6 0.52
3 7 0.39
4 0 0.38
4 7 0.37
5 1 0.32
5 4 0.35
5 7 0.28
6 0 0.58
6 2 0.4
6 4 0.93
7 2 0.34
*/