最短路问题的思路拓展
Posted akyna-zh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最短路问题的思路拓展相关的知识,希望对你有一定的参考价值。
最短路径问题的复习整理(1)
最短路径问题的复习整理(2)
最短路径问题的复习整理–优化篇
邮差
题目描述
有一个邮递员要送东西,邮局在节点 1。他总共要送 n−1 样东西,其目的地分别是节点 2 到节点 n。由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有 m 条道路。这个邮递员每次只能带一样东西,并且运送每件物品过后必须返回邮局。求送完这 n−1 样东西并且最终回到邮局最少需要的时间。
输入格式
第一行包括两个整数,n 和 m,表示城市的节点数量和道路数量。
第二行到第 (m+1) 行,每行三个整数,u,v,w,表示从 u 到 v 有一条通过时间为 w 的道路。
输出格式
输出仅一行,包含一个整数,为最少需要的时间。
说明/提示
对于 30% 的数据,1≤n≤200
对于 100%的数据,1≤n≤10^3, 1≤m≤10^5
1≤u,v≤n 1≤w≤10^4,输入保证任意两点都能互相到达。
分析过程:
1.首先考虑数据结构,这里每两个结点之间显然是有向边存储,结点直接也可以无边,可以采用邻接表实现:
struct edge{
int to, w;
edge* next;
};
struct fnode{
edge *start;
};
fnode LG[1001];
for(int i = 0; i < m; i++){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
edge *e = new edge;
e->to = b;
e->w = c;
e->next = LG[a].start;
LG[a].start = e;
}
2.然后考虑算法,最短路问题解法有三,迪杰斯特拉算法(堆优化),贝尔曼福德算法(优化spfa)以及弗洛伊德算法;
3.这里无需考虑负边,故舍弃第二个算法;
4.分析题目可知,求的是从一个点到多个点,以及多个点到一个点的最短路径,如果采用弗洛伊德算法,求出来很多不必要的结果,无疑增加了复杂度,所以可以舍弃第三个算法;
5.现在我们考虑采用迪杰斯特拉算法,思路大致就是从结点1出发一次迪杰斯特拉解决,再分别从各节点采用n-1次迪杰斯特拉,这样最终其实算法复杂度与弗洛伊德算法大致相同,但是由于可以进行优化,所以选择迪杰斯特拉算法。
代码实现:
迪杰斯特拉模板:
void shortest(int s){
memset(length, inf, sizeof(length));
memset(visited, false, sizeof(visited));
length[s] = 0;
for(int i = 1; i <= n; i++){
long long minl = inf;
int k;
for(int j = 1; j <= n; j++){
if(visited[j] != true && length[j] < minl){
minl = length[j];
k = j;
}
}
visited[k] = true;
edge *e = new edge;
e = LG[k].start;
while(e != NULL){
int u = e->to;
if(visited[u] != true && length[u] > length[k] + e->w){
length[u] = length[k] + e->w;
}
e = e->next;
}
}
return;
}
计数:
shortest(1);
for(int i = 2; i <= n; i++) least_t += length[i];
for(int i = 2; i <= n; i++){
shortest(i);
least_t += length[1];
}
结果只有40分,显然题目卡数据了
6.所以再无脑采用堆优化:
typedef pair<int, int> P;
void shortest_update(int s){
memset(length, inf, sizeof(length));
length[s] = 0;
priority_queue<P, vector<P>, greater<P> > Q;
P p_s(0, s);
Q.push(p_s);
while(!Q.empty()){
P p = Q.top();
Q.pop();
if(p.first > length[p.second]) continue;
edge *e = new edge;
e = LG[p.second].start;
while(e != NULL){
int u = e->to;
if(length[u] > e->w + p.first){
length[u] = e->w + p.first;
P p_t(length[u], u);
Q.push(p_t);
}
e = e->next;
}
}
return;
}
可惜,只有60分,虽然成功提高了20分,看来无脑解题失败了
7.思路拓展,仔细想想,我们在多对一的解决方面处理的不是很好,因为这的的确确多算了许多不必要的数据。基于此点,稍加思考,在无向图的情况下,从y点到x点的最短路径,就等于x到y的最短路径,而在有向图下,由于这每一个最短路径都是存在的且固定的,可以将最短路径上的每一条边都取反,即从y点到x点的最短路径,就等于将将该图取反后x到y的最短路径。
这里在不采用堆优化的情况下也AC了!
代码实现:
fnode LG1[1001];
fnode LG2[1001];//反图
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
edge *e1 = new edge;
e1->to = b;
e1->w = c;
e1->next = LG1[a].start;
LG1[a].start = e1;
edge *e2 = new edge;
e2->to = a;
e2->w = c;
e2->next = LG2[b].start;
LG2[b].start = e2;
}
shortest(1, 1);
for(int i = 2; i <= n; i++) least_t += length[i];
shortest(1, 2);
for(int i = 2; i <= n; i++) least_t += length[i];
printf("%lld", least_t);
void shortest(int s, int flag){
......
if(flag == 1) e = LG1[k].start;
else if(flag == 2) e = LG2[k].start;
......
}
多对一问题,逆向思维,反向建图,轻松解决。
over
以上是关于最短路问题的思路拓展的主要内容,如果未能解决你的问题,请参考以下文章