PTA 春游

春天阳光灿烂,小宋突然心血来潮想骑自行车去学校外面玩,但是到达目的地的路线不止一条,他想尽快的到达目的地,又能不需要骑太远的路, 你能帮助他吗?

输入格式:

输入包含一个测试数据,第一行有三个整数n(2 <= n <= 1000),途中可能经过的地点的个数,地点编号1~n;m(1 <= m <= 10000),为路径的条数;d(2 <= d <= n),目的地编号;其中学校为起点,默认为1。接下来m行: x y time dist , x y表示地点x,y是可以相互到达的,time,dist分别表示x到y或y到x的时间,距离。

输出格式:

按如下格式输出“花费的时间+空格+要骑的距离+空格+从学校到达目的地的路径”,路径中的两个地点之间以箭头(->)分隔。(具体见输出样例)

输入样例:

在这里给出一组输入。例如:

7 8 7

1 2 1 1

1 3 1 1

2 4 1 2

3 4 1 1

4 5 1 2 

4 6 1 1 

5 7 1 1 

6 7 2 1

输出样例:

在这里给出相应的输出。例如:

4 5 1->3->4->5->7

 想要解决这道题,大家需要先了解最重要的Dijkstra算法,那Dijkstra算法究竟该如何来理解呢?

由于本节目的是解决该问题,对于基础的Dijkstra算法我给出以下提示:(大家先要去了解最基本的Dijkastra算法程序):

①:到一个点时,选择这个点到各点的最小值

②:松弛,更新所有值,最小的为本阶段终点的最小单源路径。

Dijkstra算法的基础程序:(为了方便大家了解这道题如何用Dijkstra算法来求解,所以,我选择在解决本题的代码中提炼出基础算法的模型,大家可以跟后面解题的程序进行比较,明白怎么来化用基本的Dijkstra算法。)

void Dijkstra()
{
    int visited[n + 1];
    for(int i = 1;i < n + 1;i++)
    {
       visited[i] = 0;
    }
    visited[1] = 1;
    Time[1] = 0;

    while(1)
    {
        int MinTime = 0x3f3f3f;
        int u = -1;
        for(int i = 1;i <= n;i++)
        {
            if((visited[i] != 2) && (Time[i] < MinTime))
            {
                MinTime = Time[i];
                u = i;
            }
        }
        if(u == -1)
        {
            break;
        }
        visited[u] = 2;

        for(int i = 1;i < n + 1;i++)  //松弛
        {
            if(visited[i] != 2 && G[u][i].Time != INFTY)
            {
                if(Time[i] > Time[u] + G[u][i].Time)
                {
                    Time[i] = Time[u] + G[u][i].Time;
                    visited[i] = 1;
                    pre[i] = u;
                }
            }
        }
    }
}

那么解决本题该如何做呢?很简单,这道题最本质的实际上是双目标,保证时间最短,距离最短,但这道题很明显,距离最短是在时间最短的前提下,所以只需要判断当有多条路径都最短时间时,哪个是最短距离。所以咱们只需要在时间相等的地方加个距离判断的条件即可。如下:

void Dijkstra()
{
    int visited[n + 1];
    for(int i = 1;i < n + 1;i++)
    {
       visited[i] = 0;
    }
    visited[1] = 1;
    Dist[1] = 0;
    Time[1] = 0;

    while(1)
    {
        int MinDistance = 0x3f3f3f;
        int MinTime = 0x3f3f3f;
        int u = -1;
        for(int i = 1;i <= n;i++)
        {
            if((visited[i] != 2) && (Time[i] < MinTime))
            {
                MinTime = Time[i];
                MinDistance = Dist[i];
                u = i;
            }
            else if((visited[i] != 2) && (Time[i] == MinTime))
            {
                if (MinDistance <= Dist[i])
                {
                    MinTime = Time[i];
                    MinDistance = Dist[i];
                    u = i;
                }
            }
        }
        if(u == -1)
        {
            break;
        }
        visited[u] = 2;

        for(int i = 1;i < n + 1;i++)  //松弛
        {
            if(visited[i] != 2 && G[u][i].Time != INFTY)
            {
                if(Time[i] > Time[u] + G[u][i].Time)
                {
                    Time[i] = Time[u] + G[u][i].Time;
                    Dist[i] = Dist[u] + G[u][i].dist;
                    visited[i] = 1;
                    pre[i] = u;
                }
            }
        }
    }
}

 

 原本如果新选择的点和最短时间相同时,我们不会更换其路径,但这时我们只需要加个判断距离的条件即可。

完整代码如下:

#include <iostream>
#include <algorithm>
#define INFTY 1<<21
using namespace std;

typedef struct _Weight
{
    int Time;
    int dist;
}Weight;


Weight G[10001][10001];
int pre[10001];
void Dijkstra(int Dist[],int n,int Time[])
{
    int visited[n + 1];
    for(int i = 1;i < n + 1;i++)
    {
       visited[i] = 0;
    }
    visited[1] = 1;
    Dist[1] = 0;
    Time[1] = 0;

    while(1)
    {
        int MinDistance = 0x3f3f3f;
        int MinTime = 0x3f3f3f;
        int u = -1;
        for(int i = 1;i <= n;i++)
        {
            if((visited[i] != 2) && (Time[i] < MinTime))
            {
                MinTime = Time[i];
                MinDistance = Dist[i];
                u = i;
            }
            else if((visited[i] != 2) && (Time[i] == MinTime))
            {
                if (MinDistance >= Dist[i])
                {
                    MinTime = Time[i];
                    MinDistance = Dist[i];
                    u = i;
                }
            }
        }
        if(u == -1)
        {
            break;
        }
        visited[u] = 2;

        for(int i = 1;i < n + 1;i++)  //松弛
        {
            if(visited[i] != 2 && G[u][i].Time != INFTY)
            {
                if(Time[i] > Time[u] + G[u][i].Time)
                {
                    Time[i] = Time[u] + G[u][i].Time;
                    Dist[i] = Dist[u] + G[u][i].dist;
                    visited[i] = 1;
                    pre[i] = u;
                }
            }
        }
    }
}


void shortPathPre(int s, int v){
    if(s == v){
        cout << s;
        return;
    }
    shortPathPre(s, pre[v]);
    cout<<"->";
    cout << v;
}


int main()
{
    int n,m,end;
    cin>>n>>m>>end;
    for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= n;j++)
        {
            G[i][j].Time = INFTY;
            G[i][j].dist = INFTY;
        }
    }
    while(m--)
    {
        int x1,x2,time,distance;
        cin>>x1>>x2>>time>>distance;
        G[x1][x2].Time = time;
        G[x1][x2].dist = distance;
        G[x2][x1].Time = time;
        G[x2][x1].dist = distance;
    }
    int Dist[n + 1];
    int Time[n + 1];
    for(int i = 1;i < n + 1;i++)
    {
        Dist[i] = INFTY;
        Time[i] = INFTY;
    }
    Dijkstra(Dist,n,Time);
    cout<<Time[end]<<' '<<Dist[end]<<' ';
    shortPathPre(1,end);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值