PTA 7-35 城市间紧急救援(Dijkstra+DFS)

本题考点:

  • 多标尺的Dijkstra

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

这道题是 Dijkstra 的一个经典变形,就是在求最短单源路径的前提下,加上边权、点权、最短路径条数

关于 Dijkstra 与多标尺 的问题,在之前的文章中已经提过了,这里不再赘述原理。

我们遇到这种多标尺(第一标尺是距离)的问题,首先按照第一标尺来计算出最短路径,然后在这个过程中保存好前置的结点,随后使用 DFS 来进行计算。

完整代码如下:

#include <iostream>
#include <vector>
using namespace std;

const int maxn = 510, INF = 1e5;

int n, m, s, d;           // 城市个数(0~n-1),道路条数,出发点,目的地
int G[maxn][maxn];        // 保存城市之间的距离
int persons[maxn];        // 每个城市救援队的数目
int dis[maxn];            // 到某个点的距离
bool vis[maxn] = {false}; // 是否被访问过
vector<int> pre[maxn];    // 上一个结点

void Dijkstra(int s)
{
    fill(dis, dis + n, INF); // 初始化所有距离为无限远
    dis[s] = 0;
    for (int i = 0; i < n; i++)
    {
        int u = -1, MIN = INF;
        for (int j = 0; j < n; j++)
        { // 寻找到能到达的距离最小的点
            if (vis[j] == false && dis[j] < MIN)
            {
                u = j;
                MIN = dis[j];
            }
        }
        if (u == -1)
            break; // 所有能到达的结点都使用过了
        vis[u] = true;
        for (int v = 0; v < n; v++)
        { // 如果 k 未被访问,且经过 u 为中介点使得到 k 的距离更小,那么就更新这条线路
            if (vis[v] == false && G[u][v] != INF)
            {
                if (dis[u] + G[u][v] < dis[v])
                {
                    dis[v] = dis[u] + G[u][v];
                    pre[v].clear();
                    pre[v].push_back(u);
                }else if(dis[u] + G[u][v] == dis[v])
                {
                    pre[v].push_back(u);
                }
            }
        }
    }
}

int optValue = 0;
vector<int> tempPath, path; // 临时路径,最短路径
int num = 0;                // 记录最短路径条数
void DFS(int v)
{
    if (s == v)
    { // 递归到了出发的结点
        num++;
        tempPath.push_back(v);
        int value = 0, id;
        for (int i = 0; i < tempPath.size(); i++)
        {
            id = tempPath[i];
            value += persons[id];
        }
        if (value > optValue)
        {
            optValue = value;
            path = tempPath;
        }
        tempPath.pop_back();
    }
    else
    {
        tempPath.push_back(v);
        for (int i = 0; i < pre[v].size(); i++)
        {
            DFS(pre[v][i]);
        }
        tempPath.pop_back();
    }
}

int main()
{
    // freopen("data.txt", "r", stdin);
    scanf("%d%d%d%d", &n, &m, &s, &d);
    fill(G[0], G[0] + maxn * maxn, INF);

    int i;
    for (i = 0; i < n; i++)
    { // 读取每个城市的救援队数目
        scanf("%d", &persons[i]);
    }
    int u, v;
    for (i = 0; i < m; i++)
    { // 读取城市间的距离
        scanf("%d%d", &u, &v);
        scanf("%d", &G[u][v]);
        G[v][u] = G[u][v];
    }
    Dijkstra(s);
    DFS(d);
    printf("%d %d\n", num, optValue);
    bool flag = false;
    for (int i = path.size() - 1; i >= 0; i--)
    {
        if (flag)
        {
            printf(" %d", path[i]);
        }
        else
        {
            printf("%d", path[i]);
            flag = true;
        }
    }

    return 0;
}
posted @ 2020-04-10 10:15  南风sa  阅读(358)  评论(0编辑  收藏  举报