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

杭电  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
 

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值