1. 程式人生 > >CCF201609-4 交通規劃(100分)

CCF201609-4 交通規劃(100分)

試題編號: 201609-4
試題名稱: 交通規劃
時間限制: 1.0s
記憶體限制: 256.0MB
問題描述: 問題描述   G國國王來中國參觀後,被中國的高速鐵路深深的震撼,決定為自己的國家也建設一個高速鐵路系統。
  建設高速鐵路投入非常大,為了節約建設成本,G國國王決定不新建鐵路,而是將已有的鐵路改造成高速鐵路。現在,請你為G國國王提供一個方案,將現有的一部分鐵路改造成高速鐵路,使得任何兩個城市間都可以通過高速鐵路到達,而且從所有城市乘坐高速鐵路到首都的最短路程和原來一樣長。請你告訴G國國王在這些條件下最少要改造多長的鐵路。
輸入格式   輸入的第一行包含兩個整數n, m,分別表示G國城市的數量和城市間鐵路的數量。所有的城市由1到n編號,首都為1號。
  接下來m行,每行三個整數a, b, c,表示城市a和城市b之間有一條長度為c的雙向鐵路。這條鐵路不會經過a和b以外的城市。
輸出格式   輸出一行,表示在滿足條件的情況下最少要改造的鐵路長度。
樣例輸入 4 5
1 2 4
1 3 5
2 3 2
2 4 3
3 4 2 樣例輸出 11
評測用例規模與約定   對於20%的評測用例,1 ≤ n ≤ 10,1 ≤ m ≤ 50;
  對於50%的評測用例,1 ≤ n ≤ 100,1 ≤ m ≤ 5000;
  對於80%的評測用例,1 ≤ n ≤ 1000,1 ≤ m ≤ 50000;
  對於100%的評測用例,1 ≤ n ≤ 10000,1 ≤ m ≤ 100000,1 ≤ a, b ≤ n,1 ≤ c ≤ 1000。輸入保證每個城市都可以通過鐵路達到首都。

問題描述參見上文。

問題分析:這是一個最優化的問題,也是一個單源最短路徑問題,所有要用Dijkstra演算法。題目要求在“所有城市乘坐高速鐵路到首都的最短路程和原來一樣長”的前提下,計算出“最少要改造多少鐵路”?

程式說明:圖的表示主要有三種形式,一是鄰接表,二是鄰接矩陣,三是邊列表。鄰接矩陣對於結點多和邊少的情況都不理想。程式中用鄰接表儲存圖,即g[],是一種動態的儲存。陣列dist[]中儲存單源(首都,結點1)到各個結點(城市)的最短距離。優先佇列q按照邊的權值從小到大排隊,便於計算最短路徑。

對於n個結點的城市,要連通起來,最少有n-1條道路就夠了。

陣列cost[i]用於儲存要到達結點i,並且滿足單源最短路徑的條件,需要改造的鐵路的長度。這是使用Dijkstra演算法

解決本問題需要增加的。程式中的72行就是增加的邏輯。

另外一個問題,從單源出發到達某個結點,最短路徑有兩條以上,並且路徑長度相等時,需要選一個代價小的。例如,測試例項中,結點1到4有兩條路徑,1-2-4和1-3-4,其距離都是7,邊1-2和1-3是必選的,邊2-4和3-4是可選的,由於邊2-4的權為3,而邊3-4的權為2,所以為了到達結點4選擇小的權2。程式中,這個邏輯體現在75行。

提交後得100分的C++語言程式如下:

/* CCF201609-4 交通規劃 */

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

//#define DEBUG

const int INT_MAX2 = ((unsigned int)(-1) >> 1);
const int MAXN = 10000;

// 邊
struct _edge {
    int v, cost;
    _edge(int v2, int c){v=v2; cost=c;}
};

// 結點
struct _node {
    int u, cost;
    _node(){}
    _node(int u2, int c){u=u2; cost=c;}

    bool operator<(const _node n) const {
        return cost > n.cost;
    }
};

vector<_edge> g[MAXN+1];
priority_queue<_node> q;
int dist[MAXN+1];
int cost[MAXN+1];
bool visited[MAXN+1];

void dijkstra_add(int start, int n)
{
    for(int i=0; i<=n; i++) {
        dist[i] = INT_MAX2;
        cost[i] = INT_MAX2;
        visited[i] = false;
    }


    dist[start] = 0;
    cost[start] = 0;

    q.push(_node(start, 0));

    _node f;
    while(!q.empty()) {
        f = q.top();
        q.pop();

        int u = f.u;
        if(!visited[u]) {
            visited[u] = true;

            int len = g[u].size();
            for(int i=0; i<len; i++) {
                int v2 = g[u][i].v;

                if(visited[v2])
                    continue;

                int tempcost = g[u][i].cost;
                int nextdist = dist[u] + tempcost;

                if(dist[v2] > nextdist) {
                    dist[v2] = nextdist;
                    cost[v2] = tempcost;                      // add code
                    q.push(_node(v2, dist[v2]));
                } else if(dist[v2] == nextdist)
                    cost[v2] = min(cost[v2], tempcost); // add code
            }
        }
    }
}

int main()
{
    int n, m, src, dest, cost2;

    // 輸入資料,構建圖
    cin >> n >> m;
    for(int i=1; i<=m; i++) {
        cin >> src >> dest >> cost2;

        g[src].push_back(_edge(dest, cost2));
        g[dest].push_back(_edge(src, cost2));
    }

    // 改進的Dijkstra演算法
    dijkstra_add(1, n);

#ifdef DEBUG
    cout << "dist : ";
    for(int i=1; i<=n; i++)
        cout << dist[i] << " ";
    cout << endl;

    cout << "cost : ";
    for(int i=1; i<=n; i++)
        cout << cost[i] << " ";
    cout << endl;
#endif

    // 統計邊的權重
    int ans=0;
    for(int i=2; i<=n; i++)
        ans += cost[i];
    cout << ans << endl;

    return 0;
}