题目链接:http://www.patest.cn/contests/pat-a-practise/1018
题目:
1018. Public Bike Management (30)
There is a public bike service in Hangzhou City which provides great convenience to the tourists from all over the world. One may rent a bike at any station and return it to any other stations in the city.
The Public Bike Management Center (PBMC) keeps monitoring the real-time capacity of all the stations. A station is said to be in perfect condition if it is exactly half-full. If a station is full or empty, PBMC will collect or send bikes to adjust the condition of that station to perfect. And more, all the stations on the way will be adjusted as well.
When a problem station is reported, PBMC will always choose the shortest path to reach that station. If there are more than one shortest path, the one that requires the least number of bikes sent from PBMC will be chosen.
Figure 1
Figure 1 illustrates an example. The stations are represented by vertices and the roads correspond to the edges. The number on an edge is the time taken to reach one end station from another. The number written inside a vertex S is the current number of bikes stored at S. Given that the maximum capacity of each station is 10. To solve the problem at S3, we have 2 different shortest paths:
1. PBMC -> S1 -> S3. In this case, 4 bikes must be sent from PBMC, because we can collect 1 bike from S1 and then take 5 bikes to S3, so that both stations will be in perfect conditions.
2. PBMC -> S2 -> S3. This path requires the same time as path 1, but only 3 bikes sent from PBMC and hence is the one that will be chosen.
Input Specification:
Each input file contains one test case. For each case, the first line contains 4 numbers: Cmax (<= 100), always an even number, is the maximum capacity of each station; N (<= 500), the total number of stations; Sp, the index of the problem station (the stations are numbered from 1 to N, and PBMC is represented by the vertex 0); and M, the number of roads. The second line contains N non-negative numbers Ci (i=1,...N) where each Ci is the current number of bikes at Si respectively. Then M lines follow, each contains 3 numbers: Si, Sj, and Tij which describe the time Tij taken to move betwen stations Si and Sj. All the numbers in a line are separated by a space.
Output Specification:
For each test case, print your results in one line. First output the number of bikes that PBMC must send. Then after one space, output the path in the format: 0->S1->...->Sp. Finally after another space, output the number of bikes that we must take back to PBMC after the condition of Sp is adjusted to perfect.
Note that if such a path is not unique, output the one that requires minimum number of bikes that we must take back to PBMC. The judge's data guarantee that such a path is unique.
Sample Input:10 3 3 5 6 7 0 0 1 1 0 2 1 0 3 3 1 3 1 2 3 1Sample Output:
3 0->2->3 0
分析:
这一题我感觉是PAT Advance中最难的一题了。首先,这比一般的dijstra算法要扩展很多,不仅要求的是最短路径,而且因为要求“有多条相同路径时,都要输出”,所以这就要求每个最短路径的路径,并且还要维持一个send和back的最小值,它们之间的优先级是路径的长度>send的个数>back的个数。
所以最终代码中每个节点要维持父节点(前一个节点)和相应的路径编号。
而且,对于send,刚开始不够的要send,而不是把所有最终的平坦下来的,比如1到2到3的一条路,假设2有0个,3有10个,我们不能send和back都为0,而应该send5给1,然后再back5带回来。相当于回来的路不进行自行车的停放了。(说白了就是第一站不够的不能从第二站拿,但是第二站的可以从第一站的多的车里拿)
最后代码参考了王道论坛下载的代码,我自己写了很久写不出来,就写写注释吧
AC代码:
#include<stdio.h>
#include<math.h>
using namespace std;
struct{
int parent;
int pp;//记载前一个节点的下标,为了记载多条路径
int send;//需要运出去的自行车数
int back;//需要运回的自行车数
}record[512][512];//记载所有节点之间的值,相当于全互联的情况
int C, N, sp, M, c2, distance[512][512], cur[512];
//distance记载路径的值,直连节点之间的距离,cur是当前节点所拥有的自行车的数目
int Dis[512];//Dis记载从起点到各个点的最短距离
bool mark[512];//标注节点是否已经被包含在dijstra算法中的最短点集合中
int p[512];//0到Ni站点的路径数目
void dijkstra(int x){//x为目的站点
int newP, w, i, j, k, min, temp;
Dis[0] = 0;
record[0][0].parent = -1;
record[0][0].pp = -1;
record[0][0].send = record[0][0].back = 0;
p[0] = 1;
newP = 0;
mark[0] = true;
while (!mark[x]){//当x没有加入dijkstra已知集合时,一直循环
for (w = 1; w <= N; w++){
if (distance[newP][w] == 0 || mark[w] == true)continue;
//如果不可达或者已经在集合内,跳过
if (Dis[w] >= 0 && Dis[w] < Dis[newP] + distance[newP][w])continue;
//如果已经记载且距离比已知的距离要长,也跳过
else if (Dis[w] == -1 || Dis[w] >= Dis[newP] + distance[newP][w]){
//如果没有记载或已经记载但是有更短的路径
if (Dis[w] == -1 || Dis[w] > Dis[newP] + distance[newP][w])p[w] = 0;
//有更短的距离的话,路径数目p[w]要重新开始计数
Dis[w] = Dis[newP] + distance[newP][w];
int temp = abs(cur[w] - c2);
for (i = 0, j = p[w]; i < p[newP]; i++, j++){
//newP可能就有多个父节点,所以要对此循环
record[w][j].parent = newP;//记载当前节点的前一个节点(父节点)为newP
record[w][j].pp = i;//并计数
if (cur[w] >= c2){//如果当前站点的数目比最佳状态多,则带回的back增多,send不变
record[w][j].back = record[newP][i].back + temp;
record[w][j].send = record[newP][i].send;
}
else{
if (temp <= record[newP][i].back){//差值小于带回的,则可以把带回的车给这个站
record[w][j].back = record[newP][i].back - temp;
record[w][j].send = record[newP][i].send;
}
else{//差值大于带回的,那么指把back全给此站也不够,需要seng也增加。
record[w][j].send = record[newP][i].send + temp - record[newP][i].back;
record[w][j].back = 0;
}
}
}
p[w] = j;
}
}
min = 0x7fffffff;
for (i = 0; i <= N; i++){
if (mark[i])continue;
if (Dis[i] >= 0 && Dis[i] < min){
min = Dis[i];
newP= i;
}
}
mark[newP] = true;
}
}
int main(void){
//freopen("F://Temp/input.txt", "r", stdin);
int v, w, i, j, k, l, path[512];
int send, back, pre, prep;
scanf("%d%d%d%d", &C, &N, &sp, &M);
c2 = C / 2;//c2为最佳自行车站点状态的自行车数目
for (i = 1; i <= N; i++){
scanf("%d", &cur[i]);
Dis[i] = -1;
mark[i] = false;
p[i] = 0;
}
for (i = 1; i <= N; i++){
for (j = 1; j <= N; j++){
distance[i][j] = 0;
record[i][j].parent = record[i][j].pp = 0;
record[i][j].send = record[i][j].back = 0;
}
}
for (i = 0; i < M; i++){
scanf("%d%d%d", &v, &w, &j);
distance[w][v] = distance[v][w] = j;
}
dijkstra(sp);
send = back = 0x7fffffff;
for (i = 0; i < p[sp]; i++){//找到sp(目的节点)前的路径中send和back最小的父节点(路径)
if (send > record[sp][i].send || send == record[sp][i].send && back > record[sp][i].back){
send = record[sp][i].send;
back = record[sp][i].back;
pre = record[sp][i].parent;
prep = record[sp][i].pp;
}
}
for (i = pre, j = prep, k = 0; i >= 0;){
path[k++] = i;
pre = record[i][j].parent;
prep = record[i][j].pp;
i = pre;
j = prep;
}//通过目的地的parent找到回去的路径,并存储到path[]中
printf("%d ", send);
for (--k; k >= 0; k--){
printf("%d->", path[k]);
}
printf("%d %d\n", sp, back);
return 0;
}
截图:
P.S:
掌握基本算法是一方面,但是同时我感觉对基本算法的扩展还是要很多火候和功力的,行百里者半九十。就像你看懂了也会写了dijkstra,但是一进行加深,比如要存储多条路径的时候,就不知所措了。还要加油了。
——Apie陈小旭