题目链接: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 ", sp, back); return 0; }
截图:
P.S:
掌握基本算法是一方面。可是同一时候我感觉对基本算法的扩展还是要非常多火候和功力的,行百里者半九十。
就像你看懂了也会写了dijkstra。可是一进行加深,比方要存储多条路径的时候,就不知所措了。
还要加油了。
——Apie陈小旭