【ACM】- PAT. 1018 Public Bike Management 【圖
阿新 • • 發佈:2019-02-06
題目連結
題目分析
給出結點資訊,輸出最短路徑;
總站編號為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;
}