您的位置:首页 > 其它

杭电 hdu 3790 最短路径问题 (最短路径 + Dijkstra)稍微的变形

2015-07-14 23:27 302 查看
杭电  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)

 

Output

输出 一行有两个数, 最短距离及其花费。

 

Sample Input

3 2
1 2 5 6
2 3 4 5
1 3
0 0

 

Sample Output

9 11

 

Source

浙大计算机研究生复试上机考试-2010年

 

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;
i
4000
nt 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  杭电