目录

​1,题目描述​

​题目大意​

​输入​

​输出​

​2,思路​

​数据结构​

​关键点​

​1,求0到目的地的所有最短路径​

​2,遍历最短路径更新minNeed和minBack​

​3,代码​


1,题目描述

PAT_甲级_1018 Public Bike Management (30分) (C++)【Dijkstra保存多条最短路径信息+DFS】_PAT

PAT_甲级_1018 Public Bike Management (30分) (C++)【Dijkstra保存多条最短路径信息+DFS】_甲级_02

Sample Input:

10 3 3 5
6 7 0
0 1 1
0 2 1
0 3 3
1 3 1
2 3 1

 

Sample Output:

3 0->2->3 0

题目大意

最短路径问题,从PBMC出发,到达目的地的最短路径,若有多条最短路径,判优办法是:使沿途的站点(包括目的地)达到完美状态(站点有半数自行车,多的取出,少的补上),需要最少自行车数目的路径。

输入

第一行(4个正数):C:各个站点的容量(偶数<=100),N:站点数目(<=500),S:有问题的站点编号(编号1-N,PBMC为0),M:路的数目;

第二行:有N个数,表示各站点当前自行车数目;

剩余M行:路的两端站点的编号

输出

PBMC需要送出的自行车数目,输出路径(0->......),调整至完美状态后需送回PBMC的自行车数目

 

2,思路

这道题目看似简单,实则杀机四伏。。。还是拜读了柳神大大的解题思路。

使用dijstra算法,算出PBMC(节点0)到其余所有节点的所有最短路径。 以目的节点为根节点,节点0为叶节点(所有路径的叶节点都是节点0),使用DFS算法,遍历所有最短路径,并计算和更新minNeed与minBack。

数据结构

  • vector<int> pre[502]:存放每个节点的前驱节点,以此来代表路径。500多个节点,每个节点均声明为一个容器;
  • vector<int> path, tempPath:在dfs时,存放暂时路径以及最终路径;
  • int graph[502][502], dis[502], weight[502]:存放图,0距各站点的最短距离,各个站点的自行车数目状况;

关键点

将问题划分成两个部分:求0到目的地的所有最短路径,遍历最短路径更新minNeed和minBack。

1,求0到目的地的所有最短路径

  • 利用Dijkstra算法求最短路径:(核心内容)1,限制最外层遍历的次数(每遍历一次,确定一个点);2,寻找dis数组未遍历过的节点中的最近节点u(相对于节点0);遍历与u相邻的且未访问过节点,更新dis数组,与路径信息;
  • 利用vector<int> pre存放路径信息(使用vector便于将多个最短路径存放在一起pre[v].push_back(u));

2,遍历最短路径更新minNeed和minBack

  • 利用DFS算法,遍历从目的节点(根节点)出发的所有最短路径,并记录tempPath;
  • 当到达叶节点时,tempPath记录的即为最短路径的一种。遍历tempPath,计算need与back,并更新minNeed、minBack以及path;
  • 遍历完所有最短路径之后,minNeed、minBack以及path即为最终结果;

PAT_甲级_1018 Public Bike Management (30分) (C++)【Dijkstra保存多条最短路径信息+DFS】_dijkstra与DFS_03

3,代码

#include<iostream>
#include<stdio.h>
#include<vector>
#include<map>
#include<climits>
#include<algorithm>
using namespace std;

int c, n, s, m; //c站点容量 n站点总数 s目的站点 m路的数目
int graph[502][502], dis[502], weight[502];
bool visited[502];
vector<int> pre[502]; //记录路径
vector<int> path, tempPath;
int minNeed = INT_MAX, minBack = INT_MAX;

void dfs(int v){ //从目的节点为根节点的树 叶节点只有一个统一为0
tempPath.push_back(v);
if(v == 0){ //已经从目的地点到达起始点(相当于叶节点)
int need = 0, back = 0;
for(int i = tempPath.size() - 1; i >= 0; i-- ){
int id = tempPath[i];
if(weight[id] > 0){
back += weight[id]; //此站点超过完美状态 需要搬走
}else{
if(back > (0 - weight[id])){ //此站点未达完美状态 需要补给
back += weight[id]; //weight[id]<0
}else{
need += (0 - weight[id] - back);
back = 0;
}
}
}
if(need < minNeed){
minNeed = need;
minBack = back;
path = tempPath;
}else if(need == minNeed && back < minBack){//若需从PBMC搬运的车数相同 则选择搬回PBMC车数最少的路径
minNeed = need;
minBack = back;
path = tempPath;
}
tempPath.pop_back(); //!!!将栈中的0弹出
return;
}
for(int i = 0; i < pre[v].size(); i++) //遍历所有路径
dfs(pre[v][i]);
tempPath.pop_back(); //当一个节点的子节点全部处理完毕后 需要将其弹出 处理与它同一层的下一个节点
}

int main(){
//#ifdef ONLINE_JUDGE
//#else
// freopen("1.txt", "r", stdin);
//#endif

scanf("%d%d%d%d", &c, &n, &s, &m);
fill(dis, dis + 502, INT_MAX);
fill(graph[0], graph[0] + 502 * 502, INT_MAX);

for(int i = 1; i <= n; i++){
scanf("%d", &weight[i]);
weight[i] -= c / 2; //正数:超出完美状态 负数:低于完美状态
}

for(int i = 0; i < m; i++){
int a, b, dis;
scanf("%d%d%d", &a, &b, &dis);
graph[a][b] = graph[b][a] = dis;
}

dis[0] = 0; //起始点是哪个 哪个就置为0
for(int i = 0; i <= n; i++){ //n个自行车站点 1个PBMC
int minDis = INT_MAX;
int u = -1;
for(int j = 0; j <= n; j++){ //找出距离起始点最近的点 初始时起始点距自己最近(为0)
if(visited[j] == false && dis[j] < minDis){
u = j;
minDis = dis[j];
}
}
if(u == -1) break; //所有节点都遍历过 或者不是联通图
visited[u] = true;

for(int v = 0; v <= n; v++){ //更新与u相连 且 起始点->u->v距离 小于起始点->v 的节点
if(visited[v] == false && graph[u][v] < INT_MAX){//节点未访问过 且有通路
if(dis[v] > minDis + graph[u][v]){
dis[v] = minDis + graph[u][v];
pre[v].clear(); //更新最短路径
pre[v].push_back(u);
}
else if(dis[v] == minDis + graph[u][v]){
pre[v].push_back(u); //保存所有最短路径信息
}
}
}
}

dfs(s); //以目的节点为起始点 遍历它的所有最短路径(类似于一个树 不过叶节点只有一个 即0)
printf("%d 0", minNeed);
for(int i = path.size() - 2; i >= 0; i--){ //path.size() - 1位置处是0 上个printf中已经输出
printf("->%d", path[i]);
}
printf(" %d", minBack);
return 0;
}