题目
题目大意:
每一个自行车站有一个最大容量(偶数),当车站中车的数量为最大容量的一半时,称该状态为完美状态。若数量小于最大容量的一半,会从控制中心携带车辆来补全;若数量大于最大容量的一半,则会从该车站带走车辆。现在给出最大容量maxNum,车站数量stationNum,问题车站的编号problemIndex,边的数量以及相应的距离。要求从控制中心到问题车站的最短路径,若有多条最短路径,选择带出去最少车辆的那条,若仍有多条,选择带回最少车辆的那条。
问题分析:
本题与普通的最短路径不同,不满足最优子结构。即不能由子问题的最优解推出问题的最优解(不能根据路径上的总车数来判定minNeed和minBack)。需要通过迪杰斯特拉算法找到所有的最短路径后,通过深度优先搜索确定每条路径的need和back,再与minNeed/minBack比较
代码分析:
变量定义:
设置二维数组来存储每条路径的长度,设置vector存储每个结点的前驱结点(由于可能有多条最短路径,有多个前驱结点,不能设置成一维数组)
const int inf = 99999999;
int minNeed=inf, minBack=inf;
int road[501][501], dis[501], weight[501];
bool visit[501];
vector<int> pre[501];//用于记录每条最短路径中每个结点的前驱结点
vector<int> path, temppath;//存储路径,多设置一个temppath的原因是在后面的DFS算法中是要将最新压入的结点弹出的,但是在输出时不用
int maxNum, stationNum, problemIndex, roadNum;
用迪杰斯特拉算法寻找最短路径:
for (int i = 0; i < stationNum + 1;i++){
int u = -1, minn = inf;
for (int j = 0; j < stationNum + 1;j++){
if(visit[j]==false&&dis[j]<minn){
u = j;
minn = dis[j];
}
}
if(u==-1)
break;
visit[u] = true;
for (int j = 0; j < stationNum + 1;j++){
if(visit[j]==false&&road[u][j]!=inf){
if(dis[u]+road[u][j]<dis[j]){
dis[j] = dis[u] + road[u][j];
pre[j].clear();
pre[j].push_back(u);
}
else if(dis[u]+road[u][j]==dis[j]){
pre[j].push_back(u);
}
}
}
}
递归实现深度优先搜索:
这是本道题的一个难点,注意对递归出口的设置,注意每条最短路径遍历结束后,需要连续pop两个结点,以便下一条路径压入相应结点
void DFS(int v){
temppath.push_back(v);
if(v==0){
int need = 0, back = 0;
for (int i = temppath.size() - 1; i >= 0;i--){
//此时temppath中存储着一条最短路径,最后一个元素是0,从0开始往后推,确定need和back
int id = temppath[i];
if (weight[id] > 0)
{
back += weight[id];
}
else if (weight[id] < 0&&back>=(-1*weight[id])){
back += weight[id];
}
else if(weight[id]<0&&back<(-1*weight[id])){
need = need - (weight[id] + back);
back = 0;
}
}
//一条最短路径遍历完毕,得到need与back
if(need<minNeed){
minNeed = need;
minBack = back;
path = temppath;
}
else if(need==minNeed&&back<minBack){
minBack = back;
path = temppath;
}
temppath.pop_back();//将最后压入的0结点抛出
return ;//注意出口的设置
}
for (int j = 0; j < (int)(pre[v].size());j++){
DFS(pre[v][j]);
}//递归实现DFS
temppath.pop_back();//一个结点的所有子结点遍历完后,回到上一个结点
}
AC代码
#include<bits/stdc++.h>
using namespace std;
const int inf = 99999999;
int minNeed=inf, minBack=inf;
int road[501][501], dis[501], weight[501];
bool visit[501];
vector<int> pre[501];//用于记录每条最短路径中每个结点的前驱结点
vector<int> path, temppath;//存储路径,多设置一个temppath的原因是在后面的DFS算法中是要将最新压入的结点弹出的,但是在输出时不用
int maxNum, stationNum, problemIndex, roadNum;
void DFS(int v){
temppath.push_back(v);
if(v==0){
int need = 0, back = 0;
for (int i = temppath.size() - 1; i >= 0;i--){
//此时temppath中存储着一条最短路径,最后一个元素是0,从0开始往后推,确定need和back
int id = temppath[i];
if (weight[id] > 0)
{
back += weight[id];
}
else if (weight[id] < 0&&back>=(-1*weight[id])){
back += weight[id];
}
else if(weight[id]<0&&back<(-1*weight[id])){
need = need - (weight[id] + back);
back = 0;
}
}
//一条最短路径遍历完毕,得到need与back
if(need<minNeed){
minNeed = need;
minBack = back;
path = temppath;
}
else if(need==minNeed&&back<minBack){
minBack = back;
path = temppath;
}
temppath.pop_back();//将最后压入的0结点抛出
return ;//注意出口的设置
}
for (int j = 0; j < (int)(pre[v].size());j++){
DFS(pre[v][j]);
}//递归实现DFS
temppath.pop_back();//一个结点的所有子结点遍历完后,回到上一个结点
}//本题不满足最优子结构,无法通过单一的迪杰斯特拉算法完成,需要通过迪杰斯特拉算法找到所有的最短路径,再用深度优先搜索找到minNeed和minBack
int main(){
fill(dis, dis + 501, inf);
fill(road[0], road[0] + 501 * 501, inf);//将不存在路径赋上最大值,方便下面最短路径的判断
dis[0] = 0;
scanf("%d %d %d %d", &maxNum, &stationNum, &problemIndex, &roadNum);
for (int i = 1; i <= stationNum;i++){
scanf("%d", &weight[i]);
weight[i] = weight[i] - maxNum / 2;
}
int a, b, c;
for (int i = 0; i < roadNum;i++){
scanf("%d %d %d", &a, &b, &c);
road[a][b] = road[b][a] = c;
}
for (int i = 0; i < stationNum + 1;i++){
int u = -1, minn = inf;
for (int j = 0; j < stationNum + 1;j++){
if(visit[j]==false&&dis[j]<minn){
u = j;
minn = dis[j];
}
}
if(u==-1)
break;
visit[u] = true;
for (int j = 0; j < stationNum + 1;j++){
if(visit[j]==false&&road[u][j]!=inf){
if(dis[u]+road[u][j]<dis[j]){
dis[j] = dis[u] + road[u][j];
pre[j].clear();
pre[j].push_back(u);
}
else if(dis[u]+road[u][j]==dis[j]){
pre[j].push_back(u);
}
}
}
}
DFS(problemIndex);
printf("%d ", minNeed);
for (int i = path.size() - 1; i > 0;i--){
printf("%d->", path[i]);
}
printf("%d %d", path[0], minBack);
return 0;
}