PAT-Advanced Level-1001-Public Bike Management
题目大意:
情景是总部对目标站点及路径上的所有站点进行单车管理,规则是每个站点要有 Cmax/2 辆单车,求到目标站点最短路径上需要从总部携带的单车数及调整完运回的单车数及最短路径,存在多条最短路,取携带单车数少,运回单车数少(当携带单车数相等时)的情况。
题目分析:
1)用Dijsktra算法求出所有最短路:普通算法求得最短路唯一,用向量数组存储路径,可以有效保存全部最短路。
2)求得全部最短路后需对每一最短路DFS,运用回溯思想
3)携带单车数和运回单车数的运算:明确该调整过程是一单向过程,我WA的原因就是在计算携带单车数时,误判如果某站点有多出的单车,可以使携带单车数减少,但此忽略在本站点前的站点的补充情况。
运回单车数可理解成货车上的单车储备:单向经过每个站点,若单车不够,补一些单车,不够补说明在总部需要预留这部分单车,单车多出,多处部分拉上货车(可能是支援后面站点,可能是运回总部)
PAT甲级题解:Github
代码:
#include<iostream>
#include<vector>
#define MAX_SIZE 600
#define INF 1<<30
#define PBMC 0 //起点
using namespace std;
int Cmax, N, Sp, M;
int MinCarry_go, MinCarry_back; //最小的单车运载数,最小单车运回数
int BikeNum[MAX_SIZE]; //第i个站点自行车数量
int StationMap[MAX_SIZE][MAX_SIZE]; //站点邻接图
int PathCost[MAX_SIZE]; //起点PBMC-i的最短路径长度
bool Vis[MAX_SIZE];
vector<int> ShortestPath[MAX_SIZE]; //最短路集
void Init()
{
MinCarry_back = MinCarry_go = INF;
for (int i = 0; i < MAX_SIZE; i++)
{
Vis[i] = false;
for (int j = 0; j < MAX_SIZE; j++)
StationMap[i][j] = INF;
}
}
void Dijkstra()
{
for (int i = 0; i <= N; i++)
{
PathCost[i] = StationMap[PBMC][i];
ShortestPath[i].push_back(PBMC); //初始路径全部指向PBMC
}
Vis[PBMC] = true;
for (int i = 0; i < N; i++)
{
int Index, Min, v;
Min = INF;
for (v = 0; v <= N; v++)
{
if (!Vis[v] && PathCost[v] < Min)
{
Min = PathCost[v];
Index = v;
}
}
Vis[Index] = true;
for (v = 0; v <= N; v++)
{
if (!Vis[v] && PathCost[Index] + StationMap[Index][v] < PathCost[v]) //存在最短路刷新
{
PathCost[v] = PathCost[Index] + StationMap[Index][v];
ShortestPath[v].clear(); //清空站点v的回溯站点,表示路径唯一指向v
ShortestPath[v].push_back(Index);
}
//存在到达站点v相等的另一最短路,加入站点v的回溯(前一)站点
else if (!Vis[v] && PathCost[Index] + StationMap[Index][v] == PathCost[v])
ShortestPath[v].push_back(Index);
}
}
}
vector<int> Tmp_Path; //当前路径
vector<int> Res_Path; //最优路径
void DFS(int Now_Station)
{
if (Now_Station == 0) //到达起点
{
int Carry_go = 0, Carry_back = 0;
Tmp_Path.push_back(Now_Station); //当前tmp_path为一完整的最短路径
for (int i = Tmp_Path.size() - 1; i >= 0; i--)
{
int State = BikeNum[Tmp_Path[i]];
if (State > 0) //当前站点有多余单车可运到下一站点
Carry_back += State; //当前运回单车加上这些单车(可理解成拉货上路)
else if (State < 0) //当前站点需要单车
{
if (Carry_back > -State) //当前货车上有足够单车可以补充
Carry_back += State; //卸下这部分单车
else
{
Carry_go += -State - Carry_back; //需要在总部预备这个站点的单车补充
Carry_back = 0;
}
}
}
if (Carry_go < MinCarry_go)
{
MinCarry_go = Carry_go;
MinCarry_back = Carry_back;
Res_Path = Tmp_Path; //保存当前最优
}
else if (Carry_go == MinCarry_go && Carry_back < MinCarry_back) //携带单车相同,取需运回单车量少的
{
MinCarry_back = Carry_back;
Res_Path = Tmp_Path;
}
Tmp_Path.pop_back(); //回溯
return;
}
Tmp_Path.push_back(Now_Station);
for (int i = 0; i < ShortestPath[Now_Station].size(); i++)
DFS(ShortestPath[Now_Station][i]);
Tmp_Path.pop_back(); //回溯到上一状态
}
int main()
{
Init();
cin >> Cmax >> N >> Sp >> M;
BikeNum[0] = 0;
for (int i = 1; i <= N; i++)
{
cin >> BikeNum[i];
BikeNum[i] -= Cmax / 2; //预先减去站点所需的单车数
}
while (M--)
{
int u, v, val;
cin >> u >> v >> val;
StationMap[u][v] = StationMap[v][u] = val;
}
Dijkstra();
DFS(Sp); //从终点往前深搜
cout << MinCarry_go << " ";
for (int i = Res_Path.size() - 1; i >= 0; i--)
{
cout << Res_Path[i];
if (i)
cout << "->";
}
cout << " " << MinCarry_back << endl;
return 0;
}