1. 程式人生 > >PTA (Advanced Level)1018 Public Bike Management

PTA (Advanced Level)1018 Public Bike Management

Public Bike Management

  There is a public bike service in Hangzhou City which provides great convenience to the tourists from all over the world. One may rent a bike at any station and return it to any other stations in the city.

  The Public Bike Management Center (PBMC) keeps monitoring the real-time capacity of all the stations. A station is said to be in perfectcondition if it is exactly half-full. If a station is full or empty, PBMC will collect or send bikes to adjust the condition of that station to perfect. And more, all the stations on the way will be adjusted as well.

  When a problem station is reported, PBMC will always choose the shortest path to reach that station. If there are more than one shortest path, the one that requires the least number of bikes sent from PBMC will be chosen.

  The above figure illustrates an example. The stations are represented by vertices and the roads correspond to the edges. The number on an edge is the time taken to reach one end station from another. The number written inside a vertex S is the current number of bikes stored at S. Given that the maximum capacity of each station is 10. To solve the problem at S3​​, we have 2 different shortest paths:

  1. PBMC -> S1​​ -> S3​​. In this case, 4 bikes must be sent from PBMC, because we can collect 1 bike from S1​​ and then take 5 bikes to S3​​, so that both stations will be in perfect conditions.

  2. PBMC -> S2​​ -> S3​​. This path requires the same time as path 1, but only 3 bikes sent from PBMC and hence is the one that will be chosen.

Input Specification:

  Each input file contains one test case. For each case, the first line contains 4 numbers: Cmax​​ (≤), always an even number, is the maximum capacity of each station; N (≤), the total number of stations; Sp​​, the index of the problem station (the stations are numbered from 1 to N, and PBMC is represented by the vertex 0); and M, the number of roads. The second line contains N non-negative numbers Ci​​ (,) where each Ci​​ is the current number of bikes at Si​​ respectively. Then Mlines follow, each contains 3 numbers: Si​​, Sj​​, and Tij​​ which describe the time Tij​​ taken to move betwen stations Si​​ and Sj​​. All the numbers in a line are separated by a space.

Output Specification:

  For each test case, print your results in one line. First output the number of bikes that PBMC must send. Then after one space, output the path in the format: 0. Finally after another space, output the number of bikes that we must take back to PBMC after the condition of Sp​​ is adjusted to perfect.

  Note that if such a path is not unique, output the one that requires minimum number of bikes that we must take back to PBMC. The judge's data guarantee that such a path is unique.

Sample Input:

10 3 3 5
6 7 0
0 1 1
0 2 1
0 3 3
1 3 1
2 3 1

Sample Output:

3 0->2->3 0

解題思路:
  本題題意是,杭州市有一些共享單車站,給出車站的最大容量(一定是偶數),給出車站數量與車站之間道路的資訊,車站的最優狀態為最大容量的一半。給定目標車站,控制中心現在要攜帶或從路過的其他車站取出一些自行車,使路程中路過的所有車站和目標車站都變為最優狀態,多的自行車將被帶回控制中心。要求計算並輸出最短路徑和最短路徑情況下的攜帶數與帶回數,如果有多條最短路徑那麼選擇攜帶數最少的一條,如果還有是多條路徑,那麼選擇帶回數最少的一條。(到達目標車站後之間帶著多餘的自行車之間傳送回控制中心,不在經過任何車站)。

  輸入資訊:第一行給出4個整數,分別為 最大容量Cmax ,車站數量n, 目標車站sp,道路數量m,下一行給出n個整數代表每個車站當前自行車數量,之後跟隨m行,每行3個整數,分別為,道路連線的兩個車站和道路長度。

  車站的範圍為1 ~ n,0為控制中心。

  可以先用迪傑斯特拉獲得所有最短路徑。之後搜尋所有最短路徑獲得攜帶數(攜帶數相同獲得最小的帶回數)最小的路徑,按要求輸出。

  利用Dijkstra建立一顆最短路徑樹

void Dijkstra(int st){  //傳入起點,這裡之間傳入0控制中心即可
    dis[st] = 0;    //dis儲存控制中心到所有車站的最短距離
    //控制中心到自己的最短距離為0
    for(int i = 0; i <= n; i++){    //獲取沒有訪問過的車站中距離控制中心最近的車站
        int u = -1, minLength = INT_MAX;
        //u記錄最近的車站, minLength記錄最短距離
        for(int j = 0; j <= n; j++){
            //vis記錄車站是否已經訪問
            if(dis[j] < minLength && !vis[j]){
                u = j;
                minLength = dis[j];
            }
        }
        if(u == -1) //如果u還是-1表示其他車站和控制中心不連通
            return;
        vis[u] = true;  //將最近的車站標記為已訪問
        for(int j = 0; j <= n; j++){    //遍歷所有結點用u車站優化控制中心與其他未訪問車站的距離
            if(!vis[j] && G[u][j] != INT_MAX){  //j車站未訪問且與u車站之間有道路
                if(dis[j] > dis[u] + G[u][j]){  //判斷是否能優化
                    dis[j] = dis[u] + G[u][j];  //如果能優化
                    pre[j].clear(); //j車站的前驅車站清空並重新記錄為u
                    pre[j].push_back(u);
                }else if(dis[j] == dis[u] + G[u][j]){
                    //若不能優化但是以u為中轉是j車站到控制中心的距離和dis陣列中記錄的 j車站到控制中心的距離相等
                    //j車站的前驅增加u
                    pre[j].push_back(u);
                }
            }
        }
    }
}

  之後深搜遍歷最短路徑樹獲得攜帶數最少(如果仍有多條道路則獲得帶回數最少)的路線。

void DFS(int v){    
    if(v == 0){ //這裡我們逆序遍歷路徑,從終點車站開始到控制中心0為止
        //應為最短路徑樹中記錄控制0為葉子結點,目標車站sp為根結點
        path.push_back(0);
        judge();    //判斷當前路徑的攜帶數與帶回樹是否可以優化答案
        path.pop_back();
        return;
    }
    path.push_back(v);
    for(auto i : pre[v]){
        DFS(i);
    }
    path.pop_back();
}

  由於DFS中我們時倒序遍歷的最短路徑,所以在判斷每條路徑是否可以優化答案時,要先將DFS中獲取的路徑反轉,之後遍歷這條路徑獲得其攜帶數與帶回數即可。

void judge(){
    int need = 0, backn = 0;    //need記錄攜帶數 backn記錄帶回數
    int half = Cmax / 2;    //half記錄最優狀態車站中車輛數
    reverse(path.begin(), path.end());
    bool flag = false;  //道路開始位置為控制中心0,我們不需要將控制中心的車輛數調整為最優狀態
    //所以我們用flag = false表示正在訪問控制中心
    for(auto i : path){
        if(!flag){  //跳過控制中心
            flag = true;
            continue;
        }
        if(spValue[i] > half){  //當前車站中的共享單車數量大於最優狀態數量
            backn += spValue[i] - half; //將多出的車輛帶走
        }else{
            if(backn > half - spValue[i])   //如果當前攜帶的車輛足以將該車站補充為最優狀態
                backn -= half - spValue[i]; //當前攜帶數減去缺少車輛數
            else{
                need += (half - spValue[i]) - backn;    
                //如果不足以補足當前車站就將所有攜帶車輛放入該車站,之後還是不足的車輛從控制中心攜帶
                backn = 0;
            }
        }
    }
    if(need < minNeed){ //比較答案與當前路徑的攜帶數
        minNeed = need;
        minBack = backn;
        ansPath = path;
    }else if(need == minNeed && backn < minBack){   //攜帶數一致比較帶回數
        minBack = backn;
        ansPath = path;
    }
    reverse(path.begin(), path.end());  //為了繼續DFS重新將路徑反轉
}

  AC程式碼

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 const int maxn = 510;
  4 int n, m, Cmax, sp; //n記錄車站數量, m記錄道路數量, Cmax記錄車站最大容量(Cmax/2就是車站最優數量)
  5 int G[maxn][maxn];  //G儲存車站之間的鄰接矩陣
  6 int spValue[maxn], dis[maxn];   //spValue記錄每個車站當前的車輛數, dis記錄每個車站到控制中心的最短距離
  7 bool vis[maxn] = {false};   //vis用於在尋找最短路徑時判斷車站是否已經訪問
  8 int minNeed, minBack;
  9 vector<int> path, ansPath, pre[maxn];
 10 void Dijkstra(int st){  //傳入起點,這裡之間傳入0控制中心即可
 11     dis[st] = 0;    //dis儲存控制中心到所有車站的最短距離
 12     //控制中心到自己的最短距離為0
 13     for(int i = 0; i <= n; i++){    //獲取沒有訪問過的車站中距離控制中心最近的車站
 14         int u = -1, minLength = INT_MAX;
 15         //u記錄最近的車站, minLength記錄最短距離
 16         for(int j = 0; j <= n; j++){
 17             //vis記錄車站是否已經訪問
 18             if(dis[j] < minLength && !vis[j]){
 19                 u = j;
 20                 minLength = dis[j];
 21             }
 22         }
 23         if(u == -1) //如果u還是-1表示其他車站和控制中心不連通
 24             return;
 25         vis[u] = true;  //將最近的車站標記為已訪問
 26         for(int j = 0; j <= n; j++){    //遍歷所有結點用u車站優化控制中心與其他未訪問車站的距離
 27             if(!vis[j] && G[u][j] != INT_MAX){  //j車站未訪問且與u車站之間有道路
 28                 if(dis[j] > dis[u] + G[u][j]){  //判斷是否能優化
 29                     dis[j] = dis[u] + G[u][j];  //如果能優化
 30                     pre[j].clear(); //j車站的前驅車站清空並重新記錄為u
 31                     pre[j].push_back(u);
 32                 }else if(dis[j] == dis[u] + G[u][j]){
 33                     //若不能優化但是以u為中轉是j車站到控制中心的距離和dis陣列中記錄的 j車站到控制中心的距離相等
 34                     //j車站的前驅增加u
 35                     pre[j].push_back(u);
 36                 }
 37             }
 38         }
 39     }
 40 }
 41 void judge(){
 42     int need = 0, backn = 0;    //need記錄攜帶數 backn記錄帶回數
 43     int half = Cmax / 2;    //half記錄最優狀態車站中車輛數
 44     reverse(path.begin(), path.end());
 45     bool flag = false;  //道路開始位置為控制中心0,我們不需要將控制中心的車輛數調整為最優狀態
 46     //所以我們用flag = false表示正在訪問控制中心
 47     for(auto i : path){
 48         if(!flag){  //跳過控制中心
 49             flag = true;
 50             continue;
 51         }
 52         if(spValue[i] > half){  //當前車站中的共享單車數量大於最優狀態數量
 53             backn += spValue[i] - half; //將多出的車輛帶走
 54         }else{
 55             if(backn > half - spValue[i])   //如果當前攜帶的車輛足以將該車站補充為最優狀態
 56                 backn -= half - spValue[i]; //當前攜帶數減去缺少車輛數
 57             else{
 58                 need += (half - spValue[i]) - backn;
 59                 //如果不足以補足當前車站就將所有攜帶車輛放入該車站,之後還是不足的車輛從控制中心攜帶
 60                 backn = 0;
 61             }
 62         }
 63     }
 64     if(need < minNeed){ //比較答案與當前路徑的攜帶數
 65         minNeed = need;
 66         minBack = backn;
 67         ansPath = path;
 68     }else if(need == minNeed && backn < minBack){   //攜帶數一致比較帶回數
 69         minBack = backn;
 70         ansPath = path;
 71     }
 72     reverse(path.begin(), path.end());  //為了繼續DFS重新將路徑反轉
 73 }
 74 void DFS(int v){
 75     if(v == 0){ //這裡我們逆序遍歷路徑,從終點車站開始到控制中心0為止
 76         //應為最短路徑樹中記錄控制0為葉子結點,目標車站sp為根結點
 77         path.push_back(0);
 78         judge();    //判斷當前路徑的攜帶數與帶回樹是否可以優化答案
 79         path.pop_back();
 80         return;
 81     }
 82     path.push_back(v);
 83     for(auto i : pre[v]){
 84         DFS(i);
 85     }
 86     path.pop_back();
 87 }
 88 int main()
 89 {
 90     while(scanf("%d%d%d%d", &Cmax, &n, &sp, &m) != EOF){    //輸入最大容量 車站數量 目標車站 道路數量
 91         fill(G[0], G[0] + maxn * maxn, INT_MAX);    //將所有車站之間設為不可達
 92         for(int i = 0; i < maxn; i++){
 93             pre[i].clear(); //清空最短路徑樹
 94         }
 95         path.clear();   //清空用來記錄每個路徑的容器
 96         ansPath.clear();    //清空用來記錄答案路徑的容器
 97         for(int i = 1; i <= n; i++){
 98             scanf("%d", &spValue[i]);   //輸入每個車站當前的車輛數
 99         }
100         for(int i = 0; i < m; i++){ //輸入路徑
101             int u, v;
102             scanf("%d%d", &u, &v);  
103             scanf("%d", &G[u][v]);
104             G[v][u] = G[u][v];  //雙向路徑
105         }
106         memset(vis, false, sizeof(vis));    //將所有車站設為未訪問
107         fill(dis, dis + maxn, INT_MAX); //所有車站到控制中心的距離為無窮大
108         Dijkstra(0);    //迪傑斯特拉獲取最短路徑樹
109         minNeed = INT_MAX, minBack = INT_MAX;   //記錄答案攜帶數與帶回數為無窮多
110         DFS(sp);    //深搜最短路徑樹獲取答案
111         printf("%d ", minNeed); //輸出攜帶數
112         bool flag = false; 
113         for(auto i : ansPath){  //輸出路徑
114             if(flag)
115                 printf("->");
116             printf("%d", i);
117             flag = true;
118         }
119         printf(" %d\n", minBack);   //輸出帶回數
120     }
121     return 0;
122 }