PAT(甲) 1018 Public Bike Management (30)(詳解)

1018 Public Bike Management (30)

題目描述:

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 perfect condition 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.

這裏寫圖片描述
Figure 1 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.


  • 輸入格式
    Each input file contains one test case. For each case, the first line contains 4 numbers: Cmax (<= 100), always an even number, is the maximum capacity of each station; N (<= 500), 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 (i=1,…N) where each Ci is the current number of bikes at Si respectively. Then M lines 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.

  • 輸出格式
    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->S1->…->Sp. 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.


題目大意:
這道題目主要有3個要求:
1. 到目標點的距離儘可能短
2. 從起點出髮帶的自行車儘量少
3. 如果在帶的車相同的情況下,還有相同的路徑,就選擇帶回的車最少的路徑

解題方法:
dfs+dijstra 首先用狄傑斯特拉算法找出到目標點的最短路徑,這裏用vector來保存所有可能的路徑。然後用dfs進行搜索,用另一個vector來保存路徑(結合pop_back方法),當搜索到起點,就把路徑取出,進行最少帶回數和最少帶來車輛數的比較,最後得出結果


易錯點:
我自己犯的錯誤有
1. 在對圖進行賦值時,沒有雙向賦值。
2. back清零和take計算位置弄反,導致錯誤。
另外還有可能犯的錯誤有
3. 在計算back和take時,對tempPath進行遍歷的時候,必須要從尾遍歷,否則back和take就會計算出錯(因爲這兩者的計算是建立在運輸方向上的)


程序:

#include <stdio.h>
#include <stdlib.h>
#include <vector>
using namespace std;
#define INF 65536
int dist[501];       /* 保存各點到原點的距離 */
int collect[501];    /* 保存各點的收錄情況 */
int G[501][501];     /* 保存點之間的距離 */
int Cap[501];        /* 保存各點的車數-完美數量 */  
vector <int> path[501], tempPath, finalPath;
int back, take, MinBack = INF, MinTake = INF;

int FindMin(int N)
{   /* 尋找dist中最短路徑 */
    int Min = INF, MinIdx = -1;
    for (int i = 0; i < N; i++)
        if (collect[i] == false && dist[i] < Min)
        {   /* 未被收錄且當前距離最小 */
            Min = dist[i];
            MinIdx = i;
        }
    return MinIdx;
}

void dijstra(int N, int dest, int S)
{
    int V;
    for (int i = 0; i <= N; i++)
    {
        collect[i] = false; /* 初始化collect */
        dist[i] = G[0][i];  /* 初始化dist */
        path[i].push_back(0);   /* 初始化path */
    }
    path[S].clear();
    dist[S] = 0;    
    collect[S] = true;  /* 把起點放入集合 */
    while (1)
    {
        V = FindMin(N);
        if (V == -1 || V == dest) /* 如果找不到最短路或者已經到目標點就停止 */
            break;
        collect[V] = true;
        for (int i = 0; i <= N; i++)
            if (collect[i] == false && dist[V] + G[V][i] == dist[i])
                path[i].push_back(V);   /* 保存經過的路徑 */
            else if (collect[i] == false && dist[V] + G[V][i] < dist[i])
            {
                path[i].clear();    /* 清空向量 */
                path[i].push_back(V);
                dist[i] = dist[V] + G[V][i];    /* 更新路徑 */
            }
    }
}

void dfs(int S)
{
    tempPath.push_back(S);
    if (S == 0) /* 如果回到原點 */    
    {
        back = take = 0;
        for (int i = tempPath.size() - 1; i >= 0; i--)  /* 計算所需帶的車和帶回的車 必須反向遍歷*/
        {
            int V = tempPath[i];    /* 路徑的下標 */
            if (Cap[V] >= 0)    /* 如果該點車大於或等於完美狀態 */
                back += Cap[V];
            else    /* 如果該點的車小於完美狀態 */
            {
                if (back > -Cap[V])     /* 如果運輸車上的餘量大於經過點的缺少量 */
                    back += Cap[V];
                else
                {
                    take += -Cap[V] - back;
                    back = 0;
                }
            }   
        }
        if (take < MinTake)
        {
            MinTake = take;
            finalPath = tempPath;   /* 更新最佳路徑 */
            MinBack = back;
        }
        else if (take == MinTake && back < MinBack)     /* 如果take相同,帶回較少 */
        {
            finalPath = tempPath;   /* 更新最佳路徑 */
            MinBack = back;
        }
        tempPath.pop_back();    /* 去掉末尾元素 */
        return;/*退出當前遞歸函數(如不理解可參閱:https://blog.csdn.net/invokar/article/details/80576362)*/
    }
    for (int i = 0; i < path[S].size(); i++)
        dfs(path[S][i]);
    tempPath.pop_back();    /* 彈出上行for循環的前驅頂點 */    
}

int main(int argc, char const *argv[])
{
    int C, N, dest, M, V1, V2, D;
    scanf("%d %d %d %d", &C, &N, &dest, &M);
    for (int i = 1; i <= N; i++){
        scanf("%d", &Cap[i]);   /* 輸入每個點的自行車容量 */
        Cap[i] -= C/2;  /* 令每一個點的車輛數爲當前車輛數-完美車輛數 */
    }
    for (int i = 0; i <= N; i++)
        for (int j = 0; j <= N; j++)
            G[i][j] = INF;
    for (int i = 0; i < M; i++)
    {
        scanf("%d %d %d", &V1, &V2, &D);
        G[V1][V2] = G[V2][V1] = D;  /* 建立邊聯繫 */
    }
    dijstra(N, dest, 0);
    dfs(dest);
    printf("%d 0", MinTake);
    for (int i = finalPath.size() - 2; i >= 0 ; i--)    /* 倒着輸出 */
        printf("->%d", finalPath[i]);
    printf(" %d\n", MinBack);
    return 0;
}

如果對您有幫助,幫忙點個小拇指唄~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章