1. 程式人生 > >【ACM】- PAT. 1018 Public Bike Management 【圖

【ACM】- PAT. 1018 Public Bike Management 【圖

題目連結
題目分析

給出結點資訊,輸出最短路徑;
總站編號為0,其他站點編號為1-N

多條最短路徑時,其他標尺:
在最短路徑過程中,必須把每個結點的權值調整到題目要求的最佳;

  • 標尺一:選擇需要從總站帶出最少量的路徑
  • 標尺二:仍有多條,則選擇需要帶回最少量的路徑
  • 【陷阱】:不能在返程過程中才調整結點,去的時候就需要調整好數量,否則兩個測試點錯誤(25`)
解題思路

用 查詢所有最短路徑並儲存,之後用DFS()篩選;

篩選策略:由於不滿足最優子結構,回溯到起點,只能正向列舉完整路徑(vector<int>),逐個結點計算需求量和帶回量!

測試資料

10 4 4 5
4 8 9 0
0 1 1
1 2 1
1 3 2
2 3 1
3 4 1

輸出: 1 0->1->2->3->4 2

AC程式(C++)
/**********************************
*@ID: 3stone
*@ACM: PAT.A1018 Emergency
*@Time: 18/8/20
*@IDE: VSCode 2018 + clang++
***********************************/
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std; const int maxn = 505; const int INF = 0x7fffffff; int G[maxn][maxn]; //圖 int d[maxn]; //起點到此點的距離 vector<int> pre[maxn]; //記錄前驅結點 int vis[maxn]; //標記是否訪問 int weight[maxn]; //點的權重 int N, M, capacity, Ed, perfect_num; //結點數、邊數、最大容量、 void Dijkstra(int s) { //總站點 初始化 weight[s]不需要初始化了,因為用不到
d[s] = 0; for(int i = 0; i < N; i++) { int u = -1, MIN = INF; for(int j = 0; j <= N; j++) { if(vis[j] == false && d[j] < MIN){ u = j; MIN = d[j]; } } if(u == -1) return; vis[u] = true; for(int v = 0; v <= N; v++) { if(vis[v] == false && G[u][v] != INF){ if(d[u] + G[u][v] < d[v]) { d[v] = d[u] + G[u][v]; pre[v].clear(); pre[v].push_back(u); } else if (d[u] + G[u][v] == d[v]) { pre[v].push_back(u); //權重更小,換路 } }//if }//for - v }//for - i }//Dijkstra int min_needed, min_remain; //需求量,帶回量 vector<int> best_route; //最佳最短路徑 void dfs_route(int ed, vector<int> cur_route) { if(ed == 0) { //回溯到總站,cur_route儲存了一條最短路徑 /* 由於不滿足最優子結構,要計算需要帶出多少,只能到達起點,正向列舉最短路徑每個結點 */ //cur_route.push_back(ed); //起點不需要加入路徑不是嗎? int needed = 0, remain = 0; //正向列舉路徑 for(int i = cur_route.size() - 1; i >= 0; i--){ int id = cur_route[i]; if(weight[id] > 0) { remain += weight[id]; } else if(weight[id] < 0) { if(remain >= abs(weight[id])) { remain += weight[id]; } else { needed += (abs(weight[id]) - remain); remain = 0; } } } if(needed < min_needed) { min_needed = needed; min_remain = remain; best_route = cur_route; } else if(needed == min_needed && remain < min_remain) { min_remain = remain; best_route = cur_route; } return; } //列舉每條最短路徑點 cur_route.push_back(ed); for(int i = 0; i < pre[ed].size(); i++) { dfs_route(pre[ed][i], cur_route); } cur_route.pop_back(); } int main() { int c1, c2, L; while(scanf("%d%d%d%d", &capacity, &N, &Ed, &M) != EOF) { fill(d, d + maxn, INF); //最短距離 fill(vis, vis + maxn, false); //標記訪問 fill(G[0], G[0] + maxn * maxn, INF); //邊權 perfect_num = capacity / 2; //輸入結點權重(當前車輛數) for(int i = 1; i <= N; i++) { scanf("%d", &weight[i]); weight[i] -= perfect_num; //把點權更新為該站點車輛的需求量,正為溢位,負為缺少 } //輸入邊資訊 for(int i = 0; i < M; i++) { scanf("%d%d%d", &c1, &c2, &L); G[c1][c2] = L; //無向圖轉為 雙向邊 G[c2][c1] = L; } Dijkstra(0); min_needed = INF; min_remain = INF; best_route.clear(); vector<int> cur_route; dfs_route(Ed, cur_route); printf("%d 0->", min_needed); //需求量 for(int i = best_route.size() - 1; i > 0; i--) { printf("%d->", best_route[i]); } printf("%d", best_route[0]); printf(" %d\n", min_remain); //帶回量 } return 0; }