1030 Travel Plan (30 分)——甲级(Djkstra变形+记录路径)

14 篇文章 0 订阅

A traveler’s map gives the distances between cities along the highways, together with the cost of each highway. Now you are supposed to write a program to help a traveler to decide the shortest path between his/her starting city and the destination. If such a shortest path is not unique, you are supposed to output the one with the minimum cost, which is guaranteed to be unique.

Input Specification:
Each input file contains one test case. Each case starts with a line containing 4 positive integers N, M, S, and D, where N (≤500) is the number of cities (and hence the cities are numbered from 0 to N−1); M is the number of highways; S and D are the starting and the destination cities, respectively. Then M lines follow, each provides the information of a highway, in the format:

City1 City2 Distance Cost
where the numbers are all integers no more than 500, and are separated by a space.

Output Specification:
For each test case, print in one line the cities along the shortest path from the starting point to the destination, followed by the total distance and the total cost of the path. The numbers must be separated by a space and there must be no extra space at the end of output.

Sample Input:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
Sample Output:
0 2 3 3 40

题目大意:求最短路。如果存在多条最短路,求花费最小的路,并且输出路径。

思路:用邻接矩阵存图,dis[ ]表示源点到各点的最短路,cost[ ]表示原点到各点的最小花费,path[ ]记录前驱结点。在松弛的过程中,如果路程可以松弛,更新各个数组;如果路程相同,考虑第二因素花费:如果花费可以松弛,更新cost[ ],path[ ]。

ps:Djkstra的初始化有两种

  1. 不把源点标记:只初始化源点到自己的路径、花费等等;Djkstra进行n次
for(i=0; i<n; i++) dis[i] = i == st ? 0 : inf;
for(i=1; i<=n; i++)
{
	/* Djkstra主体部分 */
}
  1. 把源点标记:将源点到各点的信息初始化为输入进来的数据;Djkstra进行n-1次
for(i=0; i<n; i++) dis[i] = e[st][i];
book[st] = 1;
for(i=1; i<n; i++)
{
	/* Djkstra主体部分 */
}

可以这样理解:如果标记了,相当于已经用源点进行第一次松弛了,所以初始化的时候要初始化为输入进来的数据。
在写这题的过程中,由于把两种初始化搞混了,既把源点到各点的信息初始化为输入的数据,又没有标记源点,而且在进行Djkstra的时候只标记了n-1个点,所以就卡了半天。。。

代码如下:

#include <stdio.h>
#define inf 0x3f3f3f3f
#define maxn 500
void show(int *path, int i)
{
    if(path[i] == -1)
    {
        printf("%d ", i);
        return ;
    }
    show(path, path[i]);
    printf("%d ", i);
}
int main()
{
    int e[maxn][maxn], s[maxn][maxn];
    int dis[maxn], cost[maxn], book[maxn], path[maxn];
    int n, m, st, en ,i ,j;
    scanf("%d%d%d%d", &n, &m, &st, &en);
    for(i=0; i<n-1; i++)/* 图的初始化 */
    {
        e[i][i] = s[i][i] = 0;
        for(j=i+1; j<n; j++)
        {
            e[i][j] = e[j][i] = inf;
            s[i][j] = s[j][i] = inf;
        }
    }

    for(i=1; i<=m; i++)/* 读入数据 */
    {
        int a, b, d, c;
        scanf("%d%d%d%d", &a, &b, &d, &c);
        e[a][b] = e[b][a] = d;
        s[a][b] = s[b][a] = c;
    }

    for(i=0; i<n; i++)/* 采用第二种初始化,标记源点,并初始化其他点到源点的各种信息:路程、花费、前驱结点 */
    {
        book[i] = 0;
        dis[i] = e[st][i];
        cost[i] = s[st][i];
        if(dis[i] < inf) path[i] = st;
        else path[i] = -1;
    }
    path[st] = -1;
    book[st] = 1;
    /*  或者
	for(i=0; i<n; i++)
	{
		book[i] = 0;
		dis[i] = i == st ? 0 : inf;
		cost[i] = i == st ? 0 : inf;
	}
	path[st] = -1;
	for(i=1; i<=n; i++)
	{
		...
	}  */

    for(i=1; i<n; i++)
    {
        int min = inf;
        int k = -1;
        for(j=0; j<n; j++)
            if(!book[j] && min > dis[j])
            {
                min = dis[j];
                k = j;
            }
        if(k == -1) break;
        book[k] = 1;
        for(j=0; j<n ;j++)
        {
            if(!book[j] && dis[j] > dis[k] + e[k][j])/* 第一因素 */
            {
                dis[j] = dis[k] + e[k][j];
                cost[j] = cost[k] + s[k][j];
                path[j] = k;
            }
            else if(!book[j] && dis[j] == dis[k] + e[k][j])/* 第二因素 */
            {
                if(cost[j] > cost[k] + s[k][j])
                {
                    cost[j] = cost[k] + s[k][j];
                    path[j] = k;
                }
            }
        }
    }

    show(path, en);/* 递归输出路径 */
    printf("%d %d\n", dis[en], cost[en]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值