1018 Public Bike Management (30分) PAT甲级真题 dijkstra + dfs
前言:
本题是我在浏览了柳神的代码后,记下的一次半转载式笔记,不经感叹柳神的强大orz,这里给出柳神的题解地址:https://blog.csdn.net/liuchuo/article/details/52316405
题意:
有一个租借自行车的系统由n个点组成,用户可以在这n个点任意一个点借车或者还车,每个点都遵循一个共同的上限c,这里我们称一个点的状态时完美的如果这个点的车的数量为c/2,(每个点由1~n标号),而我们的出发点为0点,整个系统每次会有一个sp点发出警报,说明这个sp点的自行车数量需要调整至c/2,让我们求的时从0点出发,找一条最短的路径到达sp,调整sp以及沿途的自行车数量都达到c/2,如果车的数量不够则需要从0点调出车,(这里调整的过程是不准往回的,且到达sp点为止,后面车多了前面车少了则还是需要从0点调配出来),如果此时车多了则可以带走供给后面的车使用,到达sp点后结束,如果由多的车则全部送回,本题求的是当有一个点sp报警的时候,在最短路径的基础上,最少需要从0点调配出多少车,如果路径不唯一则选择同时送最少的车回来的路径,输出整条路径和需要送出的车数和送回的车数
分析:
本题主要由两部分构成,①首先通过dijkstra求出从0点到sp点的最短路径,在求的过程中利用一个vector数组pre[i]保存在最短路径的基础上,到达i点的所有前驱节点(因为先只考虑最短路径的情况的话可能不止有一条从0->sp的路径是最短的,每个点自然也有可能有不止一个前驱点)②通过dfs尝试每一种从0->sp的路径,计算出这条路径需要多少车need和送回多少车back,在所有的路径中选择一条need最小的,如果有并列的话,选择back最小的
注意:
虽然做题的时候我也是差不多这么想的,但是对于如何存储下每一条具体的最短路径和计算这每一条最短路径下的need和back却难住了我...还是学艺不精,我炸了,这里提醒一下需要仔细观察dfs部分的代码,这里通过一个vector数组temppath存放了每一条路径,从sp点开始,一边dfs深搜到0点,同时也将沿途的点加入到temppath中,在到达0的同时,每次都在temppath中倒着将这一条道路记录了下来,每次用完一个点之后就删去最后一个点(就像是回溯的过程)
代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<vector> 4 #include<string.h> 5 using namespace std; 6 7 const int inf = 0x3f3f3f3f; 8 int cmax, n, sp, m; 9 int minNeed = inf, minBack = inf; 10 int e[510][510], dis[510], weight[510]; 11 bool vis[510]; 12 vector<int> pre[510], path, temppath; 13 14 void dfs(int v){ 15 temppath.push_back(v); 16 if(v == 0){ 17 int need = 0, back = 0; 18 for(int i = temppath.size()-1; i >= 0; i--){ 19 int id = temppath[i]; 20 if(weight[id] > 0){ 21 back += weight[id]; 22 }else{ 23 if(back > (0 - weight[id])){//需要车且有足够的车 24 back += weight[id]; 25 }else{ //需要车但是车不够需要派出 26 need += ((0 - weight[id]) - back); 27 back = 0; 28 } 29 } 30 } 31 if(need < minNeed){ 32 minNeed = need; 33 minBack = back; 34 path = temppath; 35 }else if(need == minNeed && back < minBack){ 36 minBack = back; 37 path = temppath; 38 } 39 temppath.pop_back(); 40 return; 41 } 42 for(int i = 0; i < pre[v].size(); i++) 43 dfs(pre[v][i]); //往回找起点 0 44 temppath.pop_back(); 45 } 46 47 int main(){ 48 memset(e, inf, sizeof(e)); 49 memset(dis, inf, sizeof(dis)); 50 scanf("%d%d%d%d", &cmax, &n, &sp, &m); 51 for(int i = 1; i <= n; i++){ 52 scanf("%d", &weight[i]); 53 weight[i] = weight[i] - cmax/2; 54 } 55 for(int i = 0; i < m; i++){ 56 int a, b; 57 scanf("%d%d", &a, &b); 58 scanf("%d", &e[a][b]); 59 e[b][a] = e[a][b]; 60 } 61 dis[0] = 0; 62 for(int i = 0; i <= n; i++){ 63 int u = -1, minn = inf; 64 for(int j = 0; j <= n; j++){ 65 if(vis[j] == 0 && dis[j] < minn){ 66 minn = dis[j]; 67 u = j; 68 } 69 } 70 if(u == -1) break; 71 vis[u] = 1; 72 for(int j = 0; j <= n; j++){ 73 if(vis[j] == 0 && e[u][j] != inf){ 74 if(dis[j] > dis[u] + e[u][j]){ 75 dis[j] = dis[u] + e[u][j]; 76 pre[j].clear(); 77 pre[j].push_back(u); 78 }else if(dis[j] == dis[u] + e[u][j]){ 79 pre[j].push_back(u); 80 } 81 } 82 } 83 } 84 dfs(sp); 85 printf("%d 0", minNeed); 86 for(int i = path.size()-2; i >= 0; i--){ 87 printf("->%d", path[i]); 88 } 89 printf(" %d\n", minBack); 90 return 0; 91 }