hdu 2962 Trucking(二分+最短路)

題目

http://acm.hdu.edu.cn/showproblem.php?pid=2962

題意:卡車要運輸儘可能高的貨物(但不高於一個安全值),城市間每一條路是雙向的但是都有高度限制。求解運輸儘可能高的貨物時,卡車到達目的地的最短路徑。

解題思路

單源最短路徑的變形,在最短路徑基礎上加了高度限制。

思路:採用二分搜索法+修改的Dijkstra,取高度最大的最短路徑就是答案。(當枚舉太慢,且每次判斷都只有兩種可能時,優先考慮二分)

先求高度mid=(l+r)/2,若在mid的高度限制下,Dijkstra函數能到達終點(判斷dist[dest]的值是否有效),則繼續試探[mid+1, r]這一段,否則試探[l, mid-1]這一段。用maxH記錄每一次能通行的高度mid並更新,shorteset記錄mid高度下通行的最短路徑長度。

在Dijkstra函數的實現可以是樸素的,也可以是最小優先隊列優化的,兩者實現上分別要注意:

  • 由於涉及多次調用,因此都要把visited數組初始化爲false。
  • 鬆弛時都要保證height[pos][k]大於限制高度

  • 樸素Dijkstra:原本dist數組是初始化爲cost[s][i]的,但如果height[s][i]小於了限制高度,則要初始化爲INF,這一點需要注意!

  • 最小隊列優化Dijkstra:dist數組要初始化爲INF,因爲鬆弛是從源點開始的,只有源點的dist要先賦值爲0。

AC代碼

#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 1005, INF = 1 << 27;
typedef pair<int, int> P; //first代表距離,second代表頂點編號

int n, road;
int cost[maxn][maxn], height[maxn][maxn]; //記錄兩點間代價和高度
int dist[maxn];
bool visited[maxn];

void init()
{
    fill(height[0], height[0]+maxn, INF);
    for (int i = 0; i <= n; ++i)
        for (int j = 0; j <= n; ++j)
            cost[i][j] = (i==j) ? 0 : INF;
}

void dijkstra(int s, int h) //樸素dijkstra
{
    fill(visited, visited+maxn, false); //由於多次調用,visited每次要在函數裏初始化
    for (int i = 1; i <= n; ++i)
        dist[i] = (height[s][i] < h) ? INF : cost[s][i]; //如果高度不夠則標記無法通過
    visited[s] = true;
    while(1)
    {
        int pos = -1, mindist = INF;
        for (int i = 1; i <= n; ++i) //選出當前離源點最近的點
        {
            if (!visited[i] && dist[i] < mindist)
            {
                mindist = dist[i];
                pos = i;
            }
        }
        if (pos == -1) break; //結束
        visited[pos] = true; //標記pos訪問過
        for (int k = 1; k <= n; ++k)
        {
            if (!visited[k] && dist[pos] + cost[pos][k] < dist[k] && height[pos][k] >= h) //鬆弛要保證高度能通過!!
                dist[k] = dist[pos] + cost[pos][k];
        }
    }
}

void dijkstra_optimized(int s, int h) //最小優先隊列優化的dijkstra
{
    priority_queue<P, vector<P>, greater<P> > q;
    //初始化visited和dist!
    fill(visited, visited+maxn, false);
    fill(dist, dist+maxn, INF);
    q.push(P(0, s));
    dist[s] = 0;
    visited[s] = true;
    while(!q.empty())
    {
        P out = q.top(); q.pop();
        int pos = out.second, d = out.first;
        if (d < dist[pos]) continue; //取出的不是最短距離
        visited[pos] = true; //標記取出的pos被訪問過
        for (int k = 1; k <= n; ++k)
        {
            if (!visited[k] && d + cost[pos][k] < dist[k] && height[pos][k] >= h) //鬆弛要保證高度能通過!!
            {
                dist[k] = d + cost[pos][k];
                q.push(P(dist[k], k));
            }
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    int kase = 0;
    int a, b, h, c, src, dest, limit;
    while (cin >> n >> road && n && road)
    {
        if(kase) cout << endl;
        kase++;
        init();
        for (int i = 0; i < road; ++i)
        {
            cin >> a >> b >> h >> c;
            cost[a][b] = cost[b][a] = c;
            h = (h == -1) ? INF : h; //-1表示高度無窮大
            height[a][b] = height[b][a] = h; //記錄兩點間高度
        }
        cin >> src >> dest >> limit;
        int l = 0, r = limit;
        int shortest = INF, maxH = INF;
        //對限制的高度二分, 使其能通過且最大
        while(l <= r)
        {
            int mid = (l+r)>>1;
            dijkstra_optimized(src, mid);
            if (dist[dest] != INF)
            {
                l = mid+1; //左界擴大
                shortest = dist[dest];
                maxH = mid;
            }
            else r = mid-1; //右界縮小
        }
        cout << "Case " << kase << ":" << endl;
        if (shortest == INF || maxH == INF) //無法通過
            cout << "cannot reach destination" << endl;
        else
        {
            cout << "maximum height = " << maxH << endl;
            cout << "length of shortest route = " << shortest << endl;
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章