解法等你来战)(附Dijkstra超高质量讲解)"/>
1379:热浪(heatwv)(Dijkstra经典例题,言简意赅,史上最全,3种解法等你来战)(附Dijkstra超高质量讲解)
一,题目
1379:热浪(heatwv)
时间限制: 1000 ms 内存限制: 65536 KB
提交数: 7462 通过数: 3757
【题目描述】
德克萨斯纯朴的民眾们这个夏天正在遭受巨大的热浪!!!他们的德克萨斯长角牛吃起来不错,可是他们并不是很擅长生產富含奶油的乳製品。Farmer John此时以先天下之忧而忧,后天下之乐而乐的精神,身先士卒地承担起向德克萨斯运送大量的营养冰凉的牛奶的重任,以减轻德克萨斯人忍受酷暑的痛苦。
FJ已经研究过可以把牛奶从威斯康星运送到德克萨斯州的路线。这些路线包括起始点和终点先一共经过T (1 <= T <= 2,500)个城镇,方便地标号為1到T。除了起点和终点外的每个城镇由两条双向道路连向至少两个其它的城镇。每条道路有一个通过费用(包括油费,过路费等等)。
给定一个地图,包含C (1 <= C <= 6,200)条直接连接2个城镇的道路。每条道路由道路的起点Rs,终点Re (1 <= Rs <= T; 1 <= Re <= T),和花费(1 <= Ci <= 1,000)组成。求从起始的城镇Ts (1 <= Ts <= T)到终点的城镇Te(1 <= Te <= T)最小的总费用。
【输入】
第一行: 4个由空格隔开的整数: T, C, Ts, Te;
第2到第C+1行: 第i+1行描述第i条道路。有3个由空格隔开的整数: Rs, Re和Ci。
【输出】
一个单独的整数表示从Ts到Te的最小总费用。数据保证至少存在一条道路。
【输入样例】
7 11 5 4 2 4 2 1 4 3 7 2 2 3 4 3 5 7 5 7 3 3 6 1 1 6 3 4 2 4 3 5 6 3 7 2 1
【输出样例】
7
【提示】
【样例说明】
5->6->1->4 (3 + 1 + 3)
二,简述Dijkstra算法
Dijkstra算法算是贪心思想实现的,首先把起点到所有点的距离存下来找个最短的,然后松弛一次再找出最短的,所谓的松弛操作就是,遍历一遍看通过刚刚找到的距离最短的点作为中转站会不会更近,如果更近了就更新距离,这样把所有的点找遍之后就存下了起点到其他所有点的最短距离。
三,算法思想
迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径。它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。
有这样一个有权图,Dijkstra算法可以计算任意节点到其他节点的最短路径
算法思路
1.指定一个节点,例如我们要计算 ‘A’ 到其他节点的最短路径
2.引入两个集合(S、U),S集合包含已求出的最短路径的点(以及相应的最短长度),U集合包含未求出最短路径的点(以及A到该点的路径,注意 如上图所示,A->C由于没有直接相连 初始时为∞)
3.初始化两个集合,S集合初始时 只有当前要计算的节点,A->A = 0,U集合初始时为 A->B = 4, A->C = ∞, A->D = 2, A->E = ∞,敲黑板!!!接下来要进行核心两步骤了
4.从U集合中找出路径最短的点,加入S集合,例如 A->D = 2
5.更新U集合路径,if ( ‘D 到 B,C,E 的距离’ + ‘AD 距离’ < ‘A 到 B,C,E 的距离’ ) 则更新U
6.循环执行 4、5 两步骤,直至遍历结束,得到A 到其他节点的最短路径
算法图解
1.选定A节点并初始化,如上述步骤3所示
2.执行上述 4、5两步骤,找出U集合中路径最短的节点D 加入S集合,并根据条件 if ( ‘D 到 B,C,E 的距离’ + ‘AD 距离’ < ‘A 到 B,C,E 的距离’ ) 来更新U集合
3.这时候 A->B, A->C 都为3,没关系。其实这时候他俩都是最短距离,如果从算法逻辑来讲的话,会先取到B点。而这个时候 if 条件变成了 if ( ‘B 到 C,E 的距离’ + ‘AB 距离’ < ‘A 到 C,E 的距离’ ) ,如图所示这时候A->B距离 其实为 A->D->B
例二.这个图表示的也非常清楚。
(以第4个顶点D为起点)
初始状态:S是已计算出最短路径的顶点集合,U是未计算除最短路径的顶点的集合!
第1步:将顶点D加入到S中。
此时,S={D(0)}, U={A(∞),B(∞),C(3),E(4),F(∞),G(∞)}。 注:C(3)表示C到起点D的距离是3。
第2步:将顶点C加入到S中。
上一步操作之后,U中顶点C到起点D的距离最短;因此,将C加入到S中,同时更新U中顶点的距离。以顶点F为例,之前F到D的距离为∞;但是将C加入到S之后,F到D的距离为9=(F,C)+(C,D)。
此时,S={D(0),C(3)}, U={A(∞),B(13),E(4),F(9),G(∞)}。
第3步:将顶点E加入到S中。
上一步操作之后,U中顶点E到起点D的距离最短;因此,将E加入到S中,同时更新U中顶点的距离。还是以顶点F为例,之前F到D的距离为9;但是将E加入到S之后,F到D的距离为6=(F,E)+(E,D)。
此时,S={D(0),C(3),E(4)}, U={A(∞),B(13),F(6),G(12)}。
第4步:将顶点F加入到S中。
此时,S={D(0),C(3),E(4),F(6)}, U={A(22),B(13),G(12)}。
第5步:将顶点G加入到S中。
此时,S={D(0),C(3),E(4),F(6),G(12)}, U={A(22),B(13)}。
第6步:将顶点B加入到S中。
此时,S={D(0),C(3),E(4),F(6),G(12),B(13)}, U={A(22)}。
第7步:将顶点A加入到S中。
此时,S={D(0),C(3),E(4),F(6),G(12),B(13),A(22)}。
此时,起点D到各个顶点的最短距离就计算出来了:A(22) B(13) C(3) D(0) E(4) F(6) G(12)。
另外,这道题目是有重边的(即2点间可能存在多条路径),但是Dijkstra本身就可以去重(可以自己举例试试),所以不用特判有重边的情况
四,代码部分
1.朴素版本(邻接矩阵)
#include <bits/stdc++.h>
using namespace std;
int iu,iv,iw,n,m,s,obj,u,v,vis[10001],dis[10001],a[3333][3333],inf = 0x3f3f3f3f3f;
void dij(int s)
{for(int i = 1;i < n;i++){int p = inf;for(int j = 1;j <= n;j++)if(vis[j] == 0 && dis[j] < p){u = j;p = dis[j];}vis[u] = 1;for(int v = 1;v <= n;v++)if(vis[v] == 0 && dis[u] + a[u][v] < dis[v])dis[v] = dis[u] + a[u][v];}
}
int main()
{cin>>n>>m>>s>>obj;vis[s] = 1;for(int i = 1;i <= n;i++)for(int j = 1;j <= n;j++)if(i == j) a[i][j] = 0;else a[i][j] = inf;while(m--){cin>>iu>>iv>>iw;a[iu][iv] = a[iv][iu] = iw;}for(int i = 1;i <= n;i++) dis[i] = a[s][i];dij(s);//for(int i = 1;i <= n;i++) cout<<dis[i]<<" ";cout<<dis[obj];return 0;
}
哎,本人太弱了,一开始vis[s],a[][]忘记初始化以及dij中找到离s最近的点u后没有将vis[u]标记一下的bug都要找好久
2,邻接表版
#include <bits/stdc++.h>
using namespace std;
int iu,iv,iw,n,m,s,obj,u,v,vis[10001],dis[10001],inf = 0x3f3f3f3f3f,w[100001],vtx[100001],nxt[100001],h[100001],idx;
void add(int a,int b,int c)
{vtx[idx] = b;nxt[idx] = h[a];w[idx] = c;h[a] = idx++;
}
int find(int a,int b)
{if(a == b) return 0;int p = h[a];while(p != -1){if(vtx[p] == b) return w[p];p = nxt[p];}return inf;
}
void dij(int s)
{for(int i = 1;i < n;i++){int p = inf;for(int j = 1;j <= n;j++)if(vis[j] == 0 && dis[j] < p){u = j;p = dis[j];}vis[u] = 1;for(int v = 1;v <= n;v++)if(vis[v] == 0 && dis[u] + find(u,v) < dis[v])dis[v] = dis[u] + find(u,v);}
}
int main()
{memset(h,-1,sizeof(h));cin>>n>>m>>s>>obj;vis[s] = 1;while(m--){cin>>iu>>iv>>iw;add(iu,iv,iw);add(iv,iu,iw);}for(int i = 1;i <= n;i++) dis[i] = find(s,i);dij(s);cout<<dis[obj];return 0;
}
3,堆优化版
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;int t,p,iu,iv,iw,n,m,s,obj,u,v,vis[13003],dis[13003],pre[13003],h[13003],vtx[13003],nxt[13003],w[13003],idx,inf = 1e6 + 1;
void add(int a,int b,int c)
{vtx[idx] = b;nxt[idx] = h[a];w[idx] = c;h[a] = idx++;
}
struct node
{int u,v,dis;friend bool operator < (node a,node b){return a.dis >= b.dis;}
}tmp;
priority_queue<node> q;
void dij(int s)
{tmp.u = s;tmp.v = s;tmp.dis = 0;q.push(tmp);while(!q.empty()){tmp = q.top();q.pop();t = tmp.v;if(vis[t]) continue;vis[t] = 1;p = h[t];while(p != -1){if(vis[vtx[p]] == 0 && dis[t] + w[p] < dis[vtx[p]]){tmp.u = t;tmp.v = vtx[p];tmp.dis = dis[t] + w[p];q.push(tmp);dis[vtx[p]] = dis[t] + w[p];}p = nxt[p];}}
}
int main()
{memset(h,-1,sizeof(h));scanf("%d%d%d%d",&n,&m,&s,&obj);for(int i = 1;i <= n;i++)if(i == s) dis[i] = 0;else dis[i] = inf;for(int i = 0;i < m;i++){scanf("%d%d%d",&iu,&iv,&iw);add(iu,iv,iw);add(iv,iu,iw);}dij(s);printf("%d",dis[obj]);return 0;
}
注意:如果是邻接表存图,数组应该开大成有向图的2倍,因为邻接表是根据边来开范围的,此题又是无向图。
五,惨痛的记录
更多推荐
1379:热浪(heatwv)(Dijkstra经典例题,言简意赅,史上最全,3种解法等你来战)(附Dijkstra超高质量讲解)
发布评论