题目来源
注意点:
- 输出 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;
}