PAT(A) - 1087 All Roads Lead to Rome(30) -- Dijkstra+DFS 值得回顾

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/it2153534/article/details/82262183

1087 All Roads Lead to Rome(30)

Indeed there are many different tourist routes from our city to Rome. You are supposed to find your clients the route with the least cost while gaining the most happiness.

Input Specification:

Each input file contains one test case. For each case, the first line contains 2 positive integers N (2≤N≤200), the number of cities, and K, the total number of routes between pairs of cities; followed by the name of the starting city. The next N−1 lines each gives the name of a city and an integer that represents the happiness one can gain from that city, except the starting city. Then K lines follow, each describes a route between two cities in the format City1 City2 Cost. Here the name of a city is a string of 3 capital English letters, and the destination is always ROM which represents Rome.

Output Specification:

For each test case, we are supposed to find the route with the least cost. If such a route is not unique, the one with the maximum happiness will be recommanded. If such a route is still not unique, then we output the one with the maximum average happiness – it is guaranteed by the judge that such a solution exists and is unique.

Hence in the first line of output, you must print 4 numbers: the number of different routes with the least cost, the cost, the happiness, and the average happiness (take the integer part only) of the recommanded route. Then in the next line, you are supposed to print the route in the format City1->City2->...->ROM.

Sample Input:

6 7 HZH
ROM 100
PKN 40
GDN 55
PRS 95
BLN 80
ROM GDN 1
BLN ROM 1
HZH PKN 1
PRS ROM 2
BLN HZH 2
PKN GDN 1
HZH PRS 1

Sample Output:

3 3 195 97
HZH->PRS->ROM

大致题意为:输入一个图,每个节点和边上都有权,要求计算从出发点到“ROM”点的最短距离,在有多个最短距离时,输出路径经过城市权之和最高的,若还有相同的,则输出所经过城市个数最少的。

分析: PAT上有很多类似的题了,第一想法就是用dijkstra+dfs,曾今认为这种很难敲,没想到今天一次AC了。

  • 首先一个点到另一点的最短距离,即单源最短路径,一般都是用dijkstra算法,其用贪心思想,将时间复杂度降为O(n^2^),而相对的Foyed算法,做为多源最短路径算法,拥有O(n^3^)的时间复杂度。
  • 需要去找所经过城市权之和最高以及经过城市数最少的路径。在dijkstra算法中,可以增加一个pre[]数组,记录每个结点前驱结点以记录最短路径。然而在具有多个最短路径时,每个结点的前驱不一定只有一个,所以每个结点都需要一个记录前驱的动态数组。
  • 由终点开始,不断访问前驱数组,可以访问所有路径。此时该前驱数组更像一棵树,终点为Root结点。
  • 因此,我们可以通过DFS或BFS寻找符合要求的最短路径。对于本题来说,对最短路径的限制有两个,一个是点权之和最高(幸福值最高),另一个要求就是该路径在树中尽量的浅。显然,若不遍历整棵树我们无法找到最优路径。其中BFS在本题中依然存在内存超限的风险。因此我选择用DFS。
  • 除了核心算法以外,还有需要对输入做处理,城市需要对编号形成映射。为方便快速访问,使用map从string映射到int,使用string数组实现int映射到string。

代码如下:

#include <iostream>  
#include <cstdio>
#include <string>
#include <stack>
#include <cstring>
#include <vector>
#include <queue>
#include <set>  
#include <map>  
#include <sstream>
#include <cmath>  
#include <algorithm> 
using namespace std;

map<string, int> city;  // string -> int
string num_city[205];   // int -> string 
int happy[205] = { 0 }; // 存各顶点幸福值
int Map[205][205] = { 0 };// 邻接矩阵
bool vis[205] = { 0 };  // 应用于dijkstra算法
int dis[205] = { 0 }; 
vector<int> pre[205];   // 前驱数组,可形成树
int n, k;
int maxhappiness = 0, mincities = 300, Count = 0;  //用于dfs,寻找最大幸福值和最少城市路径。
vector<int>output;    // 最后的输出(最优路径)

void dijkstra(int start, int end)
{
    vis[start] = 1;
    for (int i = 0; i < n; i++)          // 初始化dis[]、pre[]数组
    {
        if (i == start)dis[i] = 0;
        if (Map[start][i])
        {
            dis[i] = Map[start][i];
            pre[i].push_back(start);
        }
        else dis[i] = 0xefff;
    }
    for (int j = 1; j < n; j++)      
    {
        int minl = 0xefff;
        int index = -1;
        for (int i = 0; i<n; i++)     // 找出dis[]中最小值
        {
            if (!vis[i] && minl > dis[i])
            {
                minl = dis[i];
                index = i;
            }
        }
        vis[index] = 1;
        if (index == end)return;
        for (int i = 0; i < n; i++)   // 松弛操作
        {
            if (!vis[i] && Map[index][i] && dis[i] > dis[index] + Map[index][i])
            {
                dis[i] = dis[index] + Map[index][i];
                pre[i].clear();
                pre[i].push_back(index);
            }
            else if (!vis[i] && Map[index][i] && dis[i] == dis[index] + Map[index][i])pre[i].push_back(index);
        }
    }
    return;
}

void dfs(int start, int now, int happiness, int cities, vector<int> route)
{
    if (now == start)   // 记录路径,回溯条件
    {
        Count++;
        if (happiness == maxhappiness)
        {
            if (cities < mincities)
            {
                mincities = cities;
                output.assign(route.begin(), route.end());
            }
        }
        else if (happiness > maxhappiness)
        {
            maxhappiness = happiness;
            mincities = cities;
            output.assign(route.begin(), route.end());
        }
        else return;
    }
    route.push_back(now);
    for (int i = 0; i < pre[now].size(); i++)
    {
        dfs(start, pre[now][i], happiness + happy[now], cities + 1, route);
    }
    route.pop_back();
    return;
}

int main() {

    string str;
    int start = 0, end;
    cin >> n >> k >> str;
    city[str] = 0;
    num_city[0] = str;
    for (int i = 1; i < n; i++)
    {
        string c;
        int h;
        cin >> c >> h;
        happy[i] = h;
        city[c] = i;
        num_city[i] = c;
        if (c == "ROM")end = i;
    }
    for (int i = 0; i < k; i++)
    {
        string s, e;
        int cost;
        cin >> s >> e >> cost;
        Map[city[s]][city[e]] = cost;
        Map[city[e]][city[s]] = cost;
    }
    dijkstra(start, end);
    vector<int> route;
    dfs(start, end, 0, 0, route);
    cout << Count << " " << dis[end] << " " << maxhappiness << " " << maxhappiness / mincities << endl;
    cout << num_city[start];
    for (int i = output.size() - 1; i >= 0; i--)
    {
        cout << "->" << num_city[output[i]];
    }
    cout << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/it2153534/article/details/82262183