杭电 hdu 3790 最短路径问题 (最短路径 + Dijkstra)稍微的变形
最短路径问题
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 16641 Accepted Submission(s): 4991
Problem Description
给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。
Input
输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点。n和m为0时输入结束。
(1<n<=1000, 0<m<100000, s != t)
(1<n<=1000, 0<m<100000, s != t)
Output
输出 一行有两个数, 最短距离及其花费。
Sample Input
3 2 1 2 5 6 2 3 4 5 1 3 0 0
Sample Output
9 11
Source
Recommend
notonlysuccess | We have carefully selected several similar problems for you:
1874
2066
1217
2112
1142
题意:题意很好理解,就不解释了
题解:我用的是Dijkstra算法,在存放点与点直接的关系时,使用的是静态邻接表,通过hand[]来存放每个点的连接点的头地址,再从后面连接下去,
形成类似链表形式的邻接表。将每条边存放在edge[]结构体数组中,用下表当作他们的地址,存放在hand[]中。
edge[]存放边,
其中有from,表示从这个点开始
to表示到这个地点
在Dijkstra算法中,为了使找到最小的值,采用了优先队列priority_queue<node>q;
在dis[]中,不断更新每个下标代表的地点所采取的最短路径
这道题目和hdu的2544很类似,就是多了一个关于花费的另外的条件,所以只要在最短路径相同的时候,同时考虑最小的花费就可以了
还有要是使用的是邻接矩阵,则要考虑重边的情况
Dijkstra有缺点,只能有一个源头开始,查找到其它地方的最短路径,起点始终固定,要是要求其它起点,就要从新算。
还有,就是在路径中,不能有负的值,一旦负的值,很有可能会使后面的一片全部出错
#include <queue>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn = 100005;
const int INF = 0x3f3f3f3f; //无穷大
int hand[maxn]; //每个下标表示那个点,而数组里面存的是每个点与其连接的地址的第一个
int dis[maxn]; //存放从初始点到这个下标的最短路径
int path[maxn]; //用来记录每条最短路径的经过
int vist[maxn]; //用来标记这个点是否已经遍历过了0为没有遍历,1为遍历过了
int val[maxn]; //用来记录花费
int top; //用来记录edge[]结构体数组中,队头的位置,用来确定空的位置
struct node{
int from;
int valou;
int num;
friend bool operator < (node a, node b){ //重载,用来使放进queue中的元素按照路径最短的先输出
if (a.valou == b.valou) //在路径相同的时候,注意条件换成了花费
return (a.num > b.num);
return (a.valou > b.valou);
}
};
priority_queue<node>q;
struct fun{ //用来存放每一条边的信息
int from, to;
int next; //在邻接表中用来指向下一个结构体用的
int v;
int num;
}edge[maxn];
void Add_edge (int a, int b, int v, int num){ //将边放进邻接表中
edge[top].from = a;
edge[top].to = b;
edge[top].v = v;
edge[top].num = num;
edge[top].next = hand[a]; //插入到hand[]之后的第一个,就是将后面的地址给新来的,然后将新来的地址给hand[]
hand[a] = top++; //简单的类似链表的插入步骤
}
void Init (int n, int m)
{
int i, a, b, v, num;
memset (hand, -1, sizeof (hand));
memset (path, -1, sizeof (path));
memset (vist, 0, sizeof (vist)); //使每个点都处在没有访问过的状态
memset (val, 0, sizeof (val));
top = 0;
while (m--){ //输入边,将边放进邻接表中
scanf ("%d%d%d%d", &a, &b, &v, &num);
Add_edge (a, b, v, num);
Add_edge(b, a, v, num);
}
for (i = 1; i <= n; ++i) //先赋值给每个点的距离都是无限大,然后不断求最小的
dis[i] = INF;
}
void Dijkstra (int n, int a, int b){ //Dijkstra算法
int i, f, v, to, num;
node now, then;
while (!q.empty()) q.pop(); //清空queue
now.from = a;
now.valou = 0;
now.num = 0;
q.push(now); //首先起始点传入到队列中
dis[a] = 0;
while (!q.empty()){
now = q.top();
q.pop();
f = now.from;
num = now.num;
if (vist[f]) continue; //如果这个点访问过了,就跳过
vist[f] = 1;
for (i = hand[f]; i != -1; i = edge[i].next){ //将与最短路径的点相连的点都更新一遍,将dis[]的值都更新
to = edge[i].to;
v = edge[i].v;
if (vist[to]) continue;
if (dis[f] + v < dis[to] || (dis[f] + v == dis[to] && val[to] > val[f] + edge[i].num)){ //要是这个点到和其相连的点的路径比其原先的短,就更新这个dis[]
val[to] = val[f] + edge[i].num; //如果路径相同的话,就选择花费最少的
dis[to] = dis[f] + v;
then.from = to;
then.valou = dis[to];
q.push(then); //并将这个更新后的点放入queue中
path[to] = f; //记录路径的过程
}
}
}
}
int main (){
int n, m, i, a, b;
while (scanf ("%d%d", &n, &m) != EOF){
if (n == 0 && m == 0) break;
Init (n, m);
scanf ("%d%d", &a, &b);
Dijkstra (n, a, b);
printf ("%d %d\n", dis[b], val[b]); //一次更新后,路径和花费都已经更新完,可以直接输出
}
return 0;
}