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;
}