PAT_甲级_1087 All Roads Lead to Rome

题目大意:

有N个城市,K条无向边,现在需要从某个给定的起始城市出发,前往名为"ROM"的城市,给出每条边所需要消耗的花费,求从起始城市出发,到达城市ROM所需要的最少花费,并输出最少花费的路径。如果这样的路径有多条,则选择路径上城市的幸福值之和最大的那条路径,如果路径仍然不唯一,则选择路径上城市的平均幸福值最大的那条 。

算法思路:

此题也是典型的Dijkstra算法模板题目,我们这里的最短路径变化为最小花费,不过本质都一样,依然使用dis数组存储每一个点到起点的最短距离,我们通过Dijkstra算法(只衡量第一标尺,也就是只考虑花费最短)获得起点到所有节点的最小花费,以及在此路径上的所有节点的所有前驱结点,然后使用DFS遍历前驱结点数组pre(本质上是一颗树),从终点递归访问每个结点的前驱,当访问到起点的时候,说明获得了一条路径,保存在shortest中,然后计算当前路径上的幸福感之和happ,如果当前happy比全局的最大幸福感max_happiness更大,那么更新 max_happiness = happ;min_num = shortest.size()-1;result = shortest;(保存路径),如果一样大但是平均幸福感(路径长度)比全局的平均最大幸福感(路径长度)min_num更大,也更新min_num = shortest.size()-1;result = shortest;并且得累计最短路径条数road_num。由于题目没有给定顶点范围,所以这里采用cityToIndex和intToCity保存顶点和编号(手动赋值)的双向映射以便于遍历和输出。

注意点:

  • 1、平均幸福值的计算不包括起点,因为起点的幸福值默认没有,所以是除以当前路径的结点数目-1,仔细观察样例的输出结果就知道了

提交结果:

截屏2020-11-24 下午3.27.45.png

AC代码:

#include<cstdio>
#include<vector>
#include<string>
#include<iostream>
#include<unordered_map>
#include<algorithm>

using namespace std;

int N,K,start=0,e;// 总人数,边数,起点,终点 
int happiness[300];// 点权 
int cost[300][300];// 边权 
int dis[300];// 每一个城市距离起点的最短距离
bool visited[300];// 访问标记数组
vector<int> pre[300];// 每一个结点的所有前驱 
vector<int> shortest;//最短路径
vector<int> result;// 最优路径
unordered_map<string,int> cityToIndex;// 保存城市到编号的映射 
unordered_map<int,string> intToCity;// 保存编号到城市的映射

int max_happiness = 0;
int min_num = 0x3fffffff;// 最少结点数目
int road_num = 0;// 最短路径条数 

void DFS(int s){
    shortest.push_back(s);
    if(s==start){
        ++road_num;
        // 到达起点
        int c = 0;
        int happ = 0;
        for(int i=shortest.size()-1;i>=0;--i){
            if(i>0){
                c += cost[shortest[i]][shortest[i-1]]; 
            }
            if(i!=shortest.size()-1){
                // 起点没有权值 
                happ +=  happiness[shortest[i]];
            }
        }
        if(max_happiness<happ){
            max_happiness = happ;
            min_num = shortest.size()-1;
            result = shortest;
        }else if(max_happiness==happ&&min_num>result.size()){
            min_num = shortest.size()-1;
            result = shortest;
        }
        shortest.pop_back();
        return;
    }
    for(int i=0;i<pre[s].size();++i){
        DFS(pre[s][i]);
    }
    shortest.pop_back();
} 

void Dijkstra(){
    // 初始化 
    fill(dis,dis+300,0x3fffffff);
    dis[start] = 0;
    // 循环N+1次
    for(int i=0;i<N;++i){
        // 首先从所有未访问的点中找到距离起点最小的点 
        int nearest,minDis=0x3fffffff;
        for(int j=0;j<N;++j){
            if(!visited[j]&&minDis>dis[j]){
                nearest = j;
                minDis = dis[j];
            }
        }
        if(minDis==0x3fffffff) return ;// 没有找到,说明其他点不再连通 
        // 优化借助nearest到其他未访问的点的距离
        visited[nearest] = true;
        for(int j=0;j<N;++j){
            if(!visited[j]&&cost[nearest][j]!=0){
                if(dis[j]>dis[nearest]+cost[nearest][j]){
                    dis[j] = dis[nearest]+cost[nearest][j];
                    pre[j].clear();
                    pre[j].push_back(nearest);
                }else if(dis[j]==dis[nearest]+cost[nearest][j]){
                    pre[j].push_back(nearest);
                }
            }
        }
    } 
}

int main(){
    string s;
    cin>>N>>K>>s;
    cityToIndex[s] = start;
    intToCity[start] = s;
    for(int i=1;i<N;++i){
        cin>>s>>happiness[i];
        cityToIndex[s] = i;
        intToCity[i] = s;
        if(s=="ROM") e = i;
    }
    string a,b;
    int C;
    for(int i=0;i<K;++i){
        cin>>a>>b>>C;
        int index_a = cityToIndex[a];
        int index_b = cityToIndex[b];
        cost[index_a][index_b] = cost[index_b][index_a] = C;
    }
    Dijkstra();
    DFS(e);
    printf("%d %d %d %d\n",road_num,dis[e],max_happiness,max_happiness/min_num);
    for(int i=result.size()-1;i>=0;--i){
        printf("%s",intToCity[result[i]].c_str());
        if(i>0) printf("->");
    }
    return 0;
}

主要分享个人学习经验和心得

569 声望
16 粉丝
0 条评论
推荐阅读
reactor模式
IO线程模型概述目前的IO线程处理模型一般可以分为以下三类单线程阻塞I/O服务模型多线程阻塞IO服务模型Reactor模式根据Reactor的数量和处理资源池线程的数量不同,Reactor模式有如下3种典型的实现单Reactor单线程...

乔梓鑫2阅读 4.5k评论 2

程序员适合创业吗?
大家好,我是良许。从去年 12 月开始,我已经在视频号、抖音等主流视频平台上连续更新视频到现在,并得到了不错的评价。每个视频都花了很多时间精力用心制作,欢迎大家关注哦~考虑到有些小伙伴没有看过我的视频,...

良许3阅读 1.2k

比cat更好用的命令!
但 cat 命令两个很重大的缺陷:1. 不能语法高亮输出;2. 文本太长的话无法翻页输出。正是这两个不足,使得 cat 只能用来查看行数不多的小文件。

良许2阅读 646

DBoS 系统说明
程序员TianSong以单片机开发入门,后续又做了 Qt 相关工作,有时间后开始进行 linux 相关的学习,恰巧在二一年十一月份,百问网的韦东山老师进行了三个月的 linux 驱动直播,于是有了开发 DBoS 的念头。

TianSong1阅读 1.2k

【Qt】简单桌面
[链接]简介简单桌面是一款小巧便捷的桌面背景管理软件。由编程爱好者个人开发,不收集使用者个人信息、不连接网络、不弹窗。下载功能支持单静态图片及多静态图片轮播(轮播时间可设置)支持GIF动画背景支持视频背...

TianSong3阅读 2.2k

良许翻天覆地的2022年
大家好,我是良许,新年快乐呀~在我女室友坚持不懈的努力之下,2022年的最后一天我终于被她传染了,阳了~此时的我,正顶着37多度的低烧写下这篇年终总结。2022年,对于大多数人而言,封控是主旋律——不停地核酸,...

良许2阅读 758评论 1

一个Bug让人类科技倒退几十年?
大家好,我是良许。前几天在直播的时候,问了直播间的小伙伴有没人知道「千年虫」这种神奇的「生物」的,居然没有一人能够答得上来的。所以,今天就跟大家科普一下这个人类历史上最大的 Bug 。1. 全世界的恐慌一...

良许阅读 1.4k

主要分享个人学习经验和心得

569 声望
16 粉丝
宣传栏