PAT L2-001 紧急救援(Dijkstra+路径记录)

本文详细解析了L2-001紧急救援问题的算法实现,介绍了如何通过Dijkstra算法寻找最短路径,并在路径上收集最大数量的救援队伍。代码示例展示了如何使用优先队列、松弛操作和路径记录,以解决实际救援场景中的路径规划问题。

L2-001 紧急救援 (25 分)

作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。

输入格式:

输入第一行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0 ~ (N−1);M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。

第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一。

输出格式:

第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行输出从S到D的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余空格。

输入样例:

4 5 0 3
20 30 40 10
0 1 1
1 3 2
0 3 3
0 2 2
2 3 2

 

输出样例:

2 60
0 1 3

 

这道题要求我们记录最短路径,最短路径条数,以及在最短路径下(不止一条)能够召集的最多的救援队的数目。

对于路径记录,我们可以开一个path[ ]数组,来记录城市i的上一个城市的编号,从终点开始递归打印路径。

代码

void print(int x)
    {
        if(path[x]==-1)
        {
            printf("%d",x);
            return ;
        }
        print(path[x]);
        printf(" %d",x);
        return ;
    }

 

对于最短路径的条数,分两种情况讨论:

在进行“松弛”操作时,若d[ j ]==d[ i ]+w[ i ][ j ] ,pathnum[ j ]++;

d[ j ]>d[ i ]+w[ i ][ j ] ,pathnum[ j ]=pathnum[ i ]。

 

 

AC代码

#include<bits/stdc++.h>
using namespace std;

#define rep(i,a,n) for (int i=a;i<n;i++)
const int maxn = 550;
const int INF = 1e9;

typedef struct edge
{
    int from, to, dist;
    edge(int u, int v, int d) :from(u), to(v), dist(d) {}
} edge;
typedef struct heapnode
{
    int d, u;
    bool operator <(const heapnode& rhs) const
    {
        return d > rhs.d;
    }
} heapnode;

int num[maxn];  //第i个城市的救援队的数目
int s,t;        //起点&终点
typedef struct Dijkstra
{
    int n, m;
    vector<edge>edges;
    vector<int>G[maxn];
    bool done[maxn];  //标记数组
    int d[maxn];      //起点到第i个城市的最短距离
    int val[maxn];    //从起点到第i个城市能召集的最多救援队的数量
    int path[maxn];   //第i个城市的上一个城市
    int pathnum[maxn];//最短路径的条数

    void init(int n)
    {
        this->n = n;
        rep(i, 0, n) G[i].clear();
        edges.clear();
        memset(val,0,sizeof(val));
        val[s]=num[s];
        path[s]=-1;
        pathnum[s]=1;
    }

    void add_edge(int from, int to, int dist)
    {
        edges.push_back(edge(from, to, dist));
        m = edges.size();
        G[from].push_back(m - 1);  //此处存的是边的序号
    }

    void print(int x)
    {
        if(path[x]==-1)
        {
            printf("%d",x);
            return ;
        }
        print(path[x]);
        printf(" %d",x);
        return ;
    }

    void dijkstra(int s)
    {
        priority_queue<heapnode>Q;
        rep(i, 0, n) d[i] = INF;
        d[s] = 0;
        memset(done,0,sizeof(done));
        Q.push(heapnode{ 0,s });
        while (!Q.empty())
        {
            heapnode x = Q.top();
            Q.pop();
            int u = x.u;
            if (done[u]) continue;
            done[u] = true;
            rep(i, 0, G[u].size())
            {
                edge &e = edges[G[u][i]];
                if (d[e.to] > d[u] + e.dist)
                {
                    pathnum[e.to]=pathnum[u];
                    val[e.to]=val[u]+num[e.to];
                    path[e.to]=u;
                    d[e.to] = d[u] + e.dist;
                    Q.push(heapnode{ d[e.to],e.to });
                }
                else if(d[e.to] == d[u] + e.dist)
                {
                    pathnum[e.to]+=pathnum[u];
                    if(val[e.to]<val[u]+num[e.to])
                    {
                        val[e.to]=val[u]+num[e.to];
                        path[e.to]=u;
                    }
                }
            }
        }
    }
} Dijkstra;

int main()
{
    int n,m,u,v,w;
    scanf("%d%d%d%d",&n,&m,&s,&t);
    Dijkstra D;
    Dijkstra *f=&D;
    rep(i,0,n) scanf("%d",&num[i]);
    f->init(n);
    rep(i,0,m)
    {
        scanf("%d%d%d",&u,&v,&w);
        f->add_edge(u,v,w);
        f->add_edge(v,u,w);
    }
    f->dijkstra(s);
    cout<<f->pathnum[t]<<" "<<f->val[t]<<endl;
    f->print(t);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值