一、Dijkstra算法(求单源最短路)
Dijkstra算法是贪心+广度优先搜索的最短路径算法
算法精髓:距离起点越近的点越先被扩展,可以保证所得的路径一定是最短路径
算法描述:
1.初始化:起点到起点的最短距离为0,其他为无穷大,所有点标记为未扩展
2.从未扩展的点中选择距离起点最近的点n标记为已扩展
3.尝试将与n相连的节点到起点的距离更新为更短距离(松弛),若成功则将它放入备选队列
例如:
水从1开始,经过每段河道用1秒钟
初始化
当前点编号 | 最短距离 | 前节点 |
1 | 0 | |
2 | ∞ | |
3 | ∞ | |
4 | ∞ | |
5 | ∞ |
next
当前点编号 | 最短距离 | 前节点 |
1 | 0 | |
2 | 3 | 1 |
3 | 7 | 1 |
4 | 4 | 5 |
5 | 3 | 1 |
finally
当前点编号 | 最短距离 | 前节点 |
1 | 0 | |
2 | 3 | 1 |
3 | 5 | 5 |
4 | 4 | 5 |
5 | 3 | 1 |
二、例题1
1344:【例4-4】最小花费时间限制: 1000 ms 内存限制: 65536 KB 提交数: 9326 通过数: 3983 【题目描述】在n个人中,某些人的银行账号之间可以互相转账。这些人之间转账的手续费各不相同。给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收到100元。 【输入】第一行输入两个正整数n,m,分别表示总人数和可以互相转账的人的对数。 以下m行每行输入三个正整数x,y,z,表示标号为x的人和标号为y的人之间互相转账需要扣除z%的手续费 (z<100)。 最后一行输入两个正整数A,B。数据保证A与B之间可以直接或间接地转账。 【输出】输出A使得B到账100元最少需要的总费用。精确到小数点后8位。 【输入样例】3 3
1 2 1
2 3 2
1 3 3
1 3 【输出样例】103.07153164 【提示】【数据规模】 1<=n<=2000 |
分析:构建一个图,以人为点,手续费为边,求A到B的最短距离(注意:数组用double,计算时要用乘法,不要用加法)
代码实现
#include<bits/stdc++.h>
using namespace std;
bool flag[2009];
int A,B,n,m,x,y,z;
double a[2009][2009],dist[2009];
void Dijkstra(int s, int d){
for(int i=1; i<=n; i++){
dist[i]=a[s][i];
flag[i]=false;
}
dist[s]=0;
flag[s]=true;
for(int i=1;i<=n;i++){
double maxv=0.0;
int x=d;
for(int j=1;j<=n;j++){
if(!flag[j]&&dist[j]>maxv){
maxv=dist[j];
x=j;
}
}
if(x==d)
return;
flag[x]=true;
for(int j=1; j<=n;j++){
if(!flag[j]&&a[x][j]>0){
if(dist[x]*a[x][j]>dist[j]){
dist[j]=dist[x]*a[x][j];
}
}
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>x>>y>>z;
a[x][y]=a[y][x]=0.01*(100-z);
}
cin>>A>>B;
Dijkstra(A,B);
printf("%.8lf",100/dist[B]);
return 0;
}
三、例题2
1376:信使(msner)时间限制: 1000 ms 内存限制: 65536 KB 提交数: 10588 通过数: 5314 【题目描述】战争时期,前线有n个哨所,每个哨所可能会与其他若干个哨所之间有通信联系。信使负责在哨所之间传递信息,当然,这是要花费一定时间的(以天为单位)。指挥部设在第一个哨所。当指挥部下达一个命令后,指挥部就派出若干个信使向与指挥部相连的哨所送信。当一个哨所接到信后,这个哨所内的信使们也以同样的方式向其他哨所送信。直至所有n个哨所全部接到命令后,送信才算成功。因为准备充足,每个哨所内都安排了足够的信使(如果一个哨所与其他k个哨所有通信联系的话,这个哨所内至少会配备k个信使)。 现在总指挥请你编一个程序,计算出完成整个送信过程最短需要多少时间。 【输入】第1行有两个整数n和m,中间用1个空格隔开,分别表示有n个哨所和m条通信线路,且1≤n≤100。 第2至m+1行:每行三个整数i、j、k,中间用1个空格隔开,表示第i个和第j个哨所之间存在通信线路,且这条线路要花费k天。 【输出】一个整数,表示完成整个送信过程的最短时间。如果不是所有的哨所都能收到信,就输出-1。 【输入样例】4 4
1 2 4
2 3 7
2 4 1
3 4 6 【输出样例】11 |
分析: 构建一个图,所有边设为无穷大,再输入,求一遍从指挥部到任意一个哨所的最短路
最后求出指挥部到任意哨所的最远距离即为答案
代码实现
#include<bits/stdc++.h>
using namespace std;
bool flag[2009];
int A,B,n,m,x,y,z;
int v[2009][2009],dis[2009],k,a[1009],minv,maxv=-1;
void Dijkstra(){
for(int i=1;i<=n;i++){
dis[i]=v[1][i];
}
a[1]=0;
dis[1]=0;
for(int i=1;i<n;i++){
minv=INT_MAX;
for(int j=1;j<=n;j++){
if(a[j]==0&&dis[j]<minv){
k=j;
minv=dis[j];
}
}
a[k]=1;
for(int j=1;j<=n;j++){
if(dis[j]>dis[k]+v[k][j]&&v[k][j]<INT_MAX)
dis[j]=dis[k]+v[k][j];
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
v[i][j]=INT_MAX;
dis[i]=INT_MAX;
}
}
for(int i=1;i<=m;i++){
cin>>x>>y>>z;
v[x][y]=v[y][x]=z;
}
Dijkstra();
for(int i=1;i<=n;i++){
maxv=max(maxv,dis[i]);
}
if(maxv>=INT_MAX){
cout<<"-1";
} else{
printf("%d",maxv);
}
return 0;
}