【PAT甲级真题】- All Roads Lead to Rome (30)

题目来源

All Roads Lead to Rome (30)

注意点:

  • 输出 happy 值最大的
  • happy 一样输出平均 happy 最大的

题目描述

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.

输入描述:

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.

输出描述:

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 recommended. 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 recommended route. Then in the next line, you are supposed to print the route in the format "City1->City2->...->ROM".

输入例子:

复制代码
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

输出例子:

复制代码
3 3 195 97
HZH->PRS->ROM

思路简介

dij + 保存父节点深搜

题中最短路就是 dij 的模板

但是后续要遍历所有的最短路求价值最大,必须要能够保存所有的最短路

一种常规的操作是保存每个节点的最短路父节点,然后从终点开始深搜

深搜的时候记录最小深度来代替平均值的计算

博主用的链式前向星建图,非常建议大家使用链式前向星,我后续会写相关的文章。。。大家可以自己学习一下。。。

为什么不保存子节点然后从起点深搜呢?

因为保存父节点的话,当最短路更新的时候,就直接清空当前结点的父节点数组即可,先前的父节点不是最短路

而且最短路的更新方程是 for(u):dis[v]=max(dis[v],dis[u]+w),是对父节点的遍历,保存父节点也符合更新的逻辑

但是保存子结点的话,最短路更新的时候,必须要删掉其中的部分子结点,同时可能当前结点也不是最短路的一部分,调整起来很麻烦

遇到的问题

一遍过,但是调试了很久,说说调的时候的问题

输入建图时:

  • 插入两次,是无向图

dij 方面:

  • 在距离相等时插入父节点,要判重,会出现插入相同父节点的情况
  • 用 pair 存边

深搜方面:

  • 更新价值的最大值时,同时要更新最小的深度,因为最小深度记录的是在价值最大时的最小深度

题目方面

  • 初始点不算在输入结点当中
  • 初始点没有价值,记为 0 即可
  • 输出平均值时不用小数部分
  • 路径的深度不算起点
  • 输出 path 时反向输出,因为是从终点开始搜索的

代码

cpp 复制代码
/**
 * https://www.nowcoder.com/pat/5/problem/4315
 * 状态保存dij
 */
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;

map<string,int>mp;
map<int,string>mps;

const int N=2e5;
int head[N],cnt=0,happy[N];
struct Edge{
    int next,to,k;

    Edge():next(0),to(0),k(0){};
    Edge(int next,int to,int k):next(next),to(to),k(k){}
}e[N];

void add(int u,int v,int k){
    e[cnt]=Edge(head[u],v,k);
    head[u]=cnt++;
}

int dis[N];
vector<int> pre[N];//记录父结点,深搜
void dij(int s){
    priority_queue<pii,vector<pii>,greater<pii>> pq;//pair选择dis最小的点
    pq.push({0,s});
    dis[s]=0;

    while(!pq.empty()){
        int d=pq.top().first;
        int u=pq.top().second;
        pq.pop();

        if(d>dis[u])continue;//u已经被更新过了,这个记录无效

        for(int i=head[u];i!=-1;i=e[i].next){//遍历 u 的边
            int v=e[i].to;

            if(dis[u]+e[i].k<dis[v]){
                dis[v]=dis[u]+e[i].k;
                pq.push({dis[v],v});

                //更新父结点,先前的父节点不是最短路了
                pre[v].clear();
                pre[v].emplace_back(u);
                continue;
            }

            if(dis[u]+e[i].k==dis[v]){//距离相同,插入父节点
                if(find(pre[v].begin(),pre[v].end(),u)==pre[v].end())//记得判重
                    pre[v].emplace_back(u);
            }
        }
    }
}

int max_happy=0,min_deep=0x3f3f3f3f,diffnum=0;
vector<int> path;
void dfs(int u,int start,int sum,int deep,vector<int>res){
    res.emplace_back(u);

    if(u==start){
        diffnum++;
        if(max_happy<sum){
            max_happy=sum;
            min_deep=deep;//更新最小深度
            path=res;
            return;
        }

        if(max_happy==sum&&min_deep>deep){
            min_deep=deep;
            path=res;
        }

        return;
    }

    for(auto v:pre[u]){
        dfs(v,start,sum+happy[v],deep+1,res);
    }
}

void init(){
    memset(head,-1,sizeof(head));
    memset(dis,0x3f3f3f3f,sizeof(dis));
}

void solve(){
    int n,m;
    string start_city;
    cin>>n>>m>>start_city;

    mp[start_city]=0,happy[0]=0,mps[0]=start_city;

    //建图
    for(int i=1;i<n;++i){
        string c;
        int h;
        cin>>c>>h;
        mp[c]=i,happy[i]=h,mps[i]=c;
    }

    
    for(int i=0;i<m;++i){
        string u,v;
        int k;
        cin>>u>>v>>k;
        //注意建无向图,插入两次
        add(mp[u],mp[v],k);
        add(mp[v],mp[u],k);
    }

    //dij
    dij(mp[start_city]);
    vector<int>res;
    dfs(mp["ROM"],mp[start_city],happy[mp["ROM"]],1,res);

    int len=path.size();
    cout<<diffnum<<' '<<dis[mp["ROM"]]<<' '<<max_happy<<' '<<max_happy/(min_deep-1)<<'\n';
    for(int i=len-1;i>=0;i--){
        cout<<mps[path[i]];
        if(i)cout<<"->";
    }
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    //fstream in("in.txt",ios::in);cin.rdbuf(in.rdbuf());

    init();

    int T=1;
    //cin>>T;
    while(T--){
        solve();
    }
    return 0;
}
相关推荐
xiaolang_8616_wjl8 分钟前
c++游戏_宝可梦大世界(源代码)
开发语言·c++·游戏
(●—●)橘子……9 分钟前
力扣 第525场周赛 练习理解
数据结构·python·学习·算法·leetcode
Tt有了t11 分钟前
Redis的数据结构
数据结构·数据库·redis
Rain的Java大神之路19 分钟前
LeetCode 21 合并两个有序链表:一道 Easy 题,凭什么刷掉一半候选人?
java·后端·算法
小蒋观天下26 分钟前
太阳能低功耗AI相机专项:AOV值守、唤醒时序、ISP启停时序BUG
深度学习·数码相机·算法·机器学习
wenyq734 分钟前
LeetCode 152. Maximum Product Subarray
java·算法·leetcode
余额瞒着我当琳35 分钟前
C++异常,异常的概念,异常抛出与捕获,RAII 和智能指针的设计思路,C++标准库一些指针的使用
java·开发语言·c++·算法
東隅已逝,桑榆非晚36 分钟前
map和set的使用
数据结构·c++·笔记·学习
2601_9707038341 分钟前
除湿机原理研究报告:一台机器在物理上能走多远
大数据·数据结构·算法·数据库架构·推荐算法
信奥卷王41 分钟前
[GESP202312 六级] 工作沟通(含视频)
算法