C++/PTA 直捣黄龙

简介: 本题是一部战争大片 —— 你需要从己方大本营出发,一路攻城略地杀到敌方大本营。首先时间就是生命,所以你必须选择合适的路径,以最快的速度占领敌方大本营。

题目要求

本题是一部战争大片 —— 你需要从己方大本营出发,一路攻城略地杀到敌方大本营。首先时间就是生命,所以你必须选择合适的路径,以最快的速度占领敌方大本营。当这样的路径不唯一时,要求选择可以沿途解放最多城镇的路径。若这样的路径也不唯一,则选择可以有效杀伤最多敌军的路径。


输入格式:

输入第一行给出2个正整数N(2 ≤ N ≤ 200,城镇总数)和K(城镇间道路条数),以及己方大本营和敌方大本营的代号。随后N-1行,每行给出除了己方大本营外的一个城镇的代号和驻守的敌军数量,其间以空格分隔。再后面有K行,每行按格式城镇1 城镇2 距离给出两个城镇之间道路的长度。这里设每个城镇(包括双方大本营)的代号是由3个大写英文字母组成的字符串。


输出格式:

按照题目要求找到最合适的进攻路径(题目保证速度最快、解放最多、杀伤最强的路径是唯一的),并在第一行按照格式己方大本营->城镇1->…->敌方大本营输出。第二行顺序输出最快进攻路径的条数、最短进攻距离、歼敌总数,其间以1个空格分隔,行首尾不得有多余空格。


输入样例:

10 12 PAT DBY

DBY 100

PTA 20

PDS 90

PMS 40

TAP 50

ATP 200

LNN 80

LAO 30

LON 70

PAT PTA 10

PAT PMS 10

PAT ATP 20

PAT LNN 10

LNN LAO 10

LAO LON 10

LON DBY 10

PMS TAP 10

TAP DBY 10

DBY PDS 10

PDS PTA 10

DBY ATP 10


输出样例:

PAT->PTA->PDS->DBY

3 30 210


解题思路

定义一个结构体 edge 用于存储图中的一条边,包括该边所指向的结点和边的权值。


定义一个 e 的映射结构体 map<string,vector<edge>>e 来存储图,其中 e[u] 表示结点 u 连接的所有结点及其对应的边权值。


定义各种映射或集合来存储最短距离、是否访问、城镇人数、解放城镇数量、杀敌数和最短路径条数等信息。


使用 Dijkstra 算法求解最短路径,同时根据题目要求更新城镇人数、解放城镇数量、杀敌数和最短路径条数等信息。


获取路径时,从终点开始不断向前回溯,直到回溯到起点,将每个结点加入一个 vector 中,最后依次输出即可。


输出城镇人数、解放城镇数量、杀敌数和最短路径条数等信息,即 people[ed]、city[ed]、man[ed] 和 shortpath[ed]。


代码

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define endl "\n"
const int INF=2147483647;
int n,m,num;//城镇数 道路数 
string be,ed,s;//起点 终点 城镇 
struct edge{
  string v;
  int w;
};
map<string,vector<edge>>e;//存图
//最短距离 是否访问 城镇人数 解放城镇数量 杀敌数 最短路径条数  
map<string,int>d,vis,man,city,people,shortpath;
map<string,string>pre;//保存前驱 
unordered_set<string>st;//存有什么地点
priority_queue<pair<int,string>>q;
void dijkstra(string s){
  for(auto i:st) d[i]=INF;
  d[s]=0;q.push({0,s});
  shortpath[s]=1;
  while(q.size()){
  auto t=q.top();q.pop();
  string u=t.second;
  if(vis[u]) continue;
  vis[u]=1;
  for(auto ed:e[u])
  {
    string v=ed.v;
    int w=ed.w;
    if(d[v]>d[u]+w){//距离要最短 
    d[v]=d[u]+w;
    city[v]=city[u]+1;
    people[v]=people[u]+man[v];
    shortpath[v]=shortpath[u];
    q.push({-d[v],v});
    pre[v]=u;
    }else if(d[v]==d[u]+w){//最短路径不唯一时 
    shortpath[v]+=shortpath[u];
    if(city[v]<city[u]+1){//解放城镇要最多 
      city[v]=city[u]+1;
      people[v]=people[u]+man[v];
      pre[v]=u;
    }else if(city[v]==city[u]+1){
      if(people[v]<people[u]+man[v]){//杀敌要最多 
      people[v]=people[u]+man[v];
      pre[v]=u;
      } 
    }
    }
  }
  }
}
vector<string>path;
void getpath(string s)//获取最短路径 
{
  while(s!="000"){
  path.pb(s);
  s=pre[s];
  }
}
int main()
{
  ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>m>>be>>ed;
    pre[be]="000";//将起点前驱定为000 
    st.insert(be);
    for(int i=1;i<=n-1;i++)
    {
      cin>>s>>num;
      st.insert(s);
      man[s]=num;
  }
    for(int i=0;i<m;i++)
    {
      string u,v;
      int w;
      cin>>u>>v>>w;
      e[u].pb({v,w});
      e[v].pb({u,w});
  }
  dijkstra(be);
  getpath(ed);
  for(int i=path.size()-1;i>=0;i--)
  {
  if(i!=path.size()-1) cout<<"->";
  cout<<path[i];
  }
  cout<<endl;
  cout<<shortpath[ed]<<" "<<d[ed]<<" "<<people[ed];
  return 0;
}



注意:


起点的前驱应赋为 “000”。

对于最短路径长度相同的情况,优先选择解放城镇更多的路径,其次是杀敌数更多的路径。

Dijkstra算法

Dijkstra 算法是用于求解带权有向图或无向图的单源最短路径问题的一种贪心算法。


基本思想

其基本思想是从起点开始,逐步选择未被标记为已访问的结点中距离起点最近的结点,并将其标记为已访问过,然后更新与该结点相邻的未访问结点的最短路径长度。重复这个过程直到所有结点都被标记为已访问,或者找到终点。


实现步骤

具体实现步骤如下:


初始化 dist 数组,表示起点到各个结点的最短路径长度。其中起点到自身的距离为 0,其他结点的距离为无穷大。


初始化 visited 数组,表示每个结点是否已被访问过。起点已经访问过,其他结点为未访问。


重复下述步骤,直到所有结点都被访问过:


从未访问的结点中选取距离起点最近的结点 u,并将其标记为已访问;

对于 u 的所有邻接结点 v,如果还没有被访问过,那么计算通过 u 到达 v 的路径长度,即 dist[u] + weight(u, v),并与当前记录的最短路径 dist[v] 进行比较。如果新路径更短,则更新 dist[v],并将 v 的前驱结点设置为 u。

最终得到所有结点的最短路径长度和前驱结点,可以通过回溯前驱结点的方式获取到从起点到终点的最短路径。

需要注意的是,Dijkstra 算法要求图中的边权值必须为非负数。如果存在负权边,可以使用 Bellman-Ford 算法或 SPFA 算法求解最短路径。


总结

本文使用Dijkstra算法及映射体结构实现对最短路径的求解,读者可躬身实践。

我是秋说,我们下次见。

目录
相关文章
|
3月前
|
C++
【PTA】L1-016 验证身份(C++)
【PTA】L1-016 验证身份(C++)
40 0
【PTA】L1-016 验证身份(C++)
|
3月前
|
C++
【PTA】L1-033 出生年(C++)
【PTA】L1-033 出生年(C++)
61 0
【PTA】L1-033 出生年(C++)
|
3月前
|
C++
【PTA】L1-011 A-B (C++)
【PTA】L1-011 A-B (C++)
60 0
【PTA】L1-011 A-B (C++)
|
3月前
|
C++
【PTA】​L1-005 考试座位号​ (C++)
【PTA】​L1-005 考试座位号​ (C++)
69 0
【PTA】​L1-005 考试座位号​ (C++)
|
3月前
|
测试技术 C++
【PTA】​L1-003 个位数统计​ (C++)
【PTA】​L1-003 个位数统计​ (C++)
45 0
【PTA】​L1-003 个位数统计​ (C++)
|
3月前
|
C++
【PTA】L1-020 帅到没朋友 (C++)
【PTA】L1-020 帅到没朋友 (C++)
57 0
【PTA】L1-020 帅到没朋友 (C++)
|
3月前
|
C++
【PTA】​ L1-080 乘法口诀数列​(C++)
【PTA】​ L1-080 乘法口诀数列​(C++)
49 0
【PTA】​ L1-080 乘法口诀数列​(C++)
|
3月前
|
C++
【PTA】​L1-078 吉老师的回归​(C++)
【PTA】​L1-078 吉老师的回归​(C++)
69 0
【PTA】​L1-078 吉老师的回归​(C++)
|
3月前
|
C++
【PTA】​L1-079 天梯赛的善良​ (C++)
【PTA】​L1-079 天梯赛的善良​ (C++)
67 0
【PTA】​L1-079 天梯赛的善良​ (C++)
|
3月前
|
C++
【PTA】​ L1-077 大笨钟的心情​(C++)
【PTA】​ L1-077 大笨钟的心情​(C++)
75 0
【PTA】​ L1-077 大笨钟的心情​(C++)