笔者很早就看过 狄克斯特拉 算法,本以为感觉很容易理解,但是真正写的时候,看是画的思路图写了将近一个小时,再一次被自己的逻辑思维太差。所以,写这个博客也是为了方便大家们如果能看到可以自己动动手操练一下,总没有坏处。一起加油!
去巴黎旅游的计划
题目:小明计划是去巴黎(测试样例中的终点7)旅游,但是中途可能有几个中转站,到达每个中转站所用的时间不同,所以,小明最快多长时间可以到达巴黎?
如图所示,从中转站2到达中转站5所有的时间为w8。且所有的w都有可能不一样。
利用狄克斯特拉算法求解该问题步骤是:
① 创建group、costs和perents散列表(哈希表),最后创建一个proessed散列表,其中proessed作用是记录那个中转站已经被处理,防止中转站被重复处理,同时也防止环形路线。
group:存储图中所有节点可以达到的中转站和到达该地所用是时间,意思如下表:
图中点序号 | 所能到达的点 以及 花费的时间 |
---|---|
1 | {2,w1},{3,w2},{4,w3} |
2 | {5,w8} |
3 | {2,w7},{6,w6} |
4 | {6,w4} |
… | … |
costs:存储从起点到达各个点所用的最小时间
perents:记录达到终点最短距离的路线
注意:对于costs处理方法,笔者是将所有的点插入costs中,例如{1,INT_MAX}{2,INT_MAX}这种方式插入到costs散列表中。
原因:while循环中断条件意思是所有的点均遍历过后(除了终点)结束循环。
② 首先,起点作为now_point,将起点插入proessed中,同时将从起点到邻居花费的时间更新到costs中
int form_point, goal_point,now_time;
cin>> form_point>>goal_point>> now_time;
if (form_point == goal_point) {
cout << 0 << endl;
return 0;
}
//对cost进行初始化
costs.erase(costs.find(form_point)); costs[goal_point] = INT_MAX;
int now_point = form_point;
for (unordered_map<int, int>::iterator it = group[now_point].begin(); it != group[now_point].end(); it++) {
costs[it->first] = it->second;
}
processed.insert(pair<int,int>(now_point, 0));
③ 选出costs中最小的一个点作为now_point,(目的是先选择上一步到这一步最短的距离来对costs进行更新)
pair<int, int> cost_small = {1,INT_MAX};
for (unordered_map<int, int>::iterator it = costs.begin();it != costs.end(); it++) {
//处理过的或者终点就不要处理
if (processed.find(it->first) != processed.end()|| it->first == goal_point)continue;
if (it->second < cost_small.second) {
cost_small.first = it->first;
cost_small.second = it->second;
}
}
④ 通过group找到now_point的neighbors(代码中利用cost_small)
目的是:如果选择该节点为前进对象时,则到达其邻居节点所用的时间,如果该时间比目前costs中存储的值小,则更新,且同时更新最短距离的父亲节点parents.
unordered_map<int, int> neighbors = group[cost_small.first];
for (unordered_map<int, int>::iterator it = neighbors.begin(); it != neighbors.end(); it++) {
int new_cost = cost_small.second + (it->second);
if (costs[it->first] > new_cost){
costs[it->first] = new_cost;
parents[it->first] = cost_small.first;
}
}
⑤ 最终将给节点放入已经遍历过的节点当中
processed.insert(cost_small);
完整代码如下:
/***c/c++**
* 巴黎旅行问题
* 测试用例:
5 4
1 2 2
1 3 6
2 3 3
2 4 5
3 4 1
1 4 1
复杂测试用例
8 6
1 2 2
1 3 3
3 5 7
2 5 4
2 4 6
5 4 2
5 6 6
4 6 3
1 6 0
*/
#include<iostream>
#include<map>
#include<unordered_map>
#include<vector>
using namespace std;
//unordered_map<int, int>::iterator find_lowest_cost_node(unordered_map<int, int> costs) {
//}
int main() {
int num_line, num_point;
cin >> num_line >> num_point;
//num_line代表各个点之间有多少条连线,num_point代表点的个数
//该两个参数不是很重要,如果输入是其他也是可以的
unordered_map<int, unordered_map<int, int>> group;
unordered_map<int, int> costs;
unordered_map<int, int> parents;//用于防止多次处理同一个点
unordered_map<int,int>processed;
/// 读入数据
for (int i = 1; i <= num_point; i++) {
costs.insert(pair<int,int>(i, INT_MAX));
parents.insert(pair<int, int>(i, 0));
}
for (int i = 0; i < num_line; i++) {
int begin_point,end_point,this_cost;
cin >> begin_point >> end_point >> this_cost;
if (group.find(begin_point) == group.end()) {
//新的线,插入
unordered_map<int, int> t = { pair<int,int>(end_point, this_cost) };
group.insert(pair<int, unordered_map<int,int>>(begin_point,t));
}
else {
//如果存在
group[begin_point].insert(pair<int, int>(end_point, this_cost));
}
}
//读入出发地和终止地
int form_point, goal_point,now_time;
cin>> form_point>>goal_point>> now_time;
if (form_point == goal_point) {
cout << 0 << endl;
return 0;
}
//对cost进行初始化
costs.erase(costs.find(form_point)); costs[goal_point] = INT_MAX;
int now_point = form_point;
for (unordered_map<int, int>::iterator it = group[now_point].begin(); it != group[now_point].end(); it++) {
costs[it->first] = it->second;
}
processed.insert(pair<int,int>(now_point, 0));
//如果processed数目小于总数,说明还有点没有遍历到
while (processed.size()!=num_point-1) {
//找最近的点
pair<int, int> cost_small = {1,INT_MAX};
for (unordered_map<int, int>::iterator it = costs.begin();it != costs.end(); it++) {
//处理过的或者终点就不要处理
if (processed.find(it->first) != processed.end()|| it->first == goal_point)continue;
if (it->second < cost_small.second) {
cost_small.first = it->first;
cost_small.second = it->second;
}
}
unordered_map<int, int> neighbors = group[cost_small.first];
for (unordered_map<int, int>::iterator it = neighbors.begin(); it != neighbors.end(); it++) {
int new_cost = cost_small.second + (it->second);
if (costs[it->first] > new_cost){
costs[it->first] = new_cost;
parents[it->first] = cost_small.first;
}
}
processed.insert(cost_small);
}
cout << now_time+costs[goal_point] << endl;
return 0;
}