我的PAT-ADVANCED代码仓:https://github.com/617076674/PAT-ADVANCED
原题链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805489282433024
题目描述:
题目翻译:
1018 公共自行车管理
杭州的公共自行车服务给来自世界各地的游客带来了便利。人们可以在任意一个公共自行车站点租借自行车并在任意一个其他公共自行车站点归还自行车。公共自行车管理中心(PBMC)实时观察者所有公共自行车站点的自行车数量。如果一个公共自行车站点的公共自行车数量是该站点最大可停放量的一半,我们认为该站点处于“完美”状态。如果一个公共自行车站点的公共自行车数量为空或者满了,PBMC会增加或减少该站点的公共自行车数量使其处于“完美”状态。同时,在PBMC到该站点的路径上的所有公共自行车站点也会被调整至“完美”状态。
当一个公共自行车站点发生了问题,PBMC会选择到达那个站点的最短路径。如果有多条最短路径,PBMC会选择那条需要提供最少公共自行车数量的路径。
上述图形展示了一个例子。点代表公共自行车站点,边代表路径。边上的数字代表这条路径所需花费的时间。点中的数字代表当前该点拥有的公共自行车数量。每个站点最大的公共自行车容量都是10。为了解决S3中发生的问题,我们有两条最短路径:
1.PBMC -> S1 -> S3。在这条路径中PBMC必须提供4辆公共自行车,因为我们可以把S1站点中的一辆放在S3站点,再额外提供4辆,于是每个站点都处于了“完美”状态。
2.PBMC -> S2 -> S3。这条路径和第1条路径花费的时间相同,但是PBMC只需提供3辆公共自行车,因此PBMC会选择这条路径。
输入格式:
每个输入文件包含一个测试用例。在每个测试用例中,第一行包含4个数字:Cmax(<= 100),是一个偶数,代表每个站点的最大公共自行车容量;N(<= 500),总站点数;Sp,发生问题的站点编号(每个站点编号为1 ~ N,PBMC的编号为0);M,路径数量。第二行包含了N个非负数字Ci(i = 1, ..., N),代表每个站点目前拥有的公共自行车数量。接下来的M行,每行包含3个数字Si、Sj和Tij,表示从站点Si到Sj需要花费时间Tij。一行中的所有数字都由一个空格隔开。
输出格式:
对每个测试用例,在一行中打印你的结果。首先打印出PBMC需要提供的公共自行车数量。然后隔一个空格,以如下形式输出路径:0−>S1−>⋯−>Sp。最后再隔一个空格,在调整Sp站点至完美状态后,我们需要带回到PBMC的公共自行车数量。
注意如果路径不唯一,输出那条我们需要带回到PBMC的公共自行车数量最少的路径。测试数据保证这样的路径是唯一的。
输入样例:
10 3 3 5
6 7 0
0 1 1
0 2 1
0 3 3
1 3 1
2 3 1
输出样例:
3 0->2->3 0
知识点:Dijkstra算法、Bellman-Ford算法、SPFA算法、深度优先遍历
思路一:Dijkstra算法+深度优先遍历(邻接表实现)
本题是常规的最短路径问题,难点在于第二条件和第三条件的确定。
一开始,我以为只需要计算路径中所有公共自行车站点已有的公共自行车数量,再和其满容量的一半作差。如果是正数,那么我们就需要回收相应数量的公共自行车。如果是负数,我们就需要提供相应数量的公共自行车。如果是0,我们既不需要提供也不需要回收自行车。这其实是不对的,考虑下述情况:(数字代表站点已有的公共自行车数量,每个站点的满容量假设为10)
3->2->10
按我刚刚的逻辑,10 + 3 + 2 == 15,我们既不需要提供也不需要回收自行车。但事实上题目的要求是,当我们第一次走到3这个节点时,我们就需要给其提供2辆公共自行车。同理,当我们第一次走到2这个节点时,我们就需要给其提供3辆自行车。因此我们需要提供的自行车数量总数是5。而对于10这个节点,我们需要回收5辆自行车。因此这种情况应该是提供5辆自行车,又回收5辆自行车。
因此我们求得第二第三条件的逻辑应该这样:
(1)设置第二条件,提供自行车数量的最优值初值optValue1 = INF,这里INF为我自定义的一个无穷大数。设置第三条件,回收自行车数量的最优值初值optValue2 = INF。
(2)在深度优先遍历到达递归终点的时候,我们需要按如下步骤计算这条路径需要提供和回收的自行车数量。
a:设需要提供的自行车数量provide初值为0,需要回收的自行车数量初值recycle也为0。
b:如果当前节点拥有的自行车数量大于或等于最大容量的一半,那么当前节点需要回收对应差值的自行车数量,recycle加上相应的值。
c:如果当前节点拥有的自行车数量小于最大容量的一半,我们需要判断我们当前回收来的自行车数量能否填补这个空缺,即recycle是否大于等于其差值。
c-1:如果我们当前回收来的自行车数量能够填补这个空缺,那么只需要recycle值减去相应的值即可。
c-2:如果我们当前回收来的自行车数量无法填补这个空缺,那么就需要我们额外提供自行车,该数量为所有回收来的自行车都填补这个空缺后,填补该空缺还需要的自行车数量。还有,由于recycle全部用于填补了空缺,其值需要置0。
(3)如果当前路径的provide小于optValue1,那么我们以该路径作为最优路径,同时更新optValue1和optValue2的值。
(4)如果当前路径的provide等于optValue1,且当前路径的recycle值小于optValue2,我们也以该路径作为最优路径,同时更新optValue2的值。
正是由于第二条件和第三条件与整条路径相关,因此我们只能通过Dijkstra算法+深度优先遍历的方式来解决这个问题,而不能单纯地用Dijkstra算法解决此问题。
时间复杂度是O(N ^ 2)。空间复杂度是O(N + M)。
C++代码:
#include<iostream>
#include<vector>
using namespace std;
struct node {
int v; //节点编号
int time; //边权
node(int _v, int _time) : v(_v), time(_time) {}; //构造函数
};
int Cmax; //每个站点的最大容量
int N; //站点数量
int Sp; //目的地
int M; //道路数量
int INF = 1000000000; //定义无穷大数
int C[502] = {0}; //存放每个站点当前自行车数量
vector<node> graph[502]; //无向图
bool visited[502] = {false}; //标记数组
int d[502]; //记录最短路径长度
vector<int> pre[502]; //记录前一个节点
vector<int> tempPath;
vector<