1. 程式人生 > >bzoj1003: [ZJOI2006]物流運輸(DP + 最短路)

bzoj1003: [ZJOI2006]物流運輸(DP + 最短路)

bzoj1003

題目描述:物流公司要把一批貨物從碼頭A運到碼頭B。由於貨物量比較大,需要n天才能運完。貨物運輸過程中一般要轉停好幾個碼頭。物流公司通常會設計一條固定的運輸路線,以便對整個運輸過程實施嚴格的管理和跟蹤。由於各種因素的存在,有的時候某個碼頭會無法裝卸貨物。這時候就必須修改運輸路線,讓貨物能夠按時到達目的地。但是修改路線是一件十分麻煩的事情,會帶來額外的成本。因此物流公司希望能夠訂一個n天的運輸計劃,使得總成本儘可能地小。

輸入格式:第一行是四個整數n(1<=n<=100)、m(1<=m<=20)、K和e。n表示貨物運輸所需天數,m表示碼頭總數,K表示每次修改運輸路線所需成本。接下來e行每行是一條航線描述,包括了三個整數,依次表示航線連線的兩個碼頭編號以及航線長度(>0)。其中碼頭A編號為1,碼頭B編號為m。單位長度的運輸費用為1。航線是雙向的。再接下來一行是一個整數d,後面的d行每行是三個整數P( 1 < P < m)、a、b(1< = a < = b < = n)。表示編號為P的碼頭從第a天到第b天無法裝卸貨物(含頭尾)。同一個碼頭有可能在多個時間段內不可用。但任何時間都存在至少一條從碼頭A到碼頭B的運輸路線。

輸出格式:包括了一個整數表示最小的總成本。總成本=n天運輸路線長度之和+K*改變運輸路線的次數。

輸入樣例:
5 5 10 8
1 2 1
1 3 3
1 4 2
2 3 2
2 4 4
3 4 1
3 5 2
4 5 2
4
2 2 3
3 1 1
3 3 3
4 4 5

輸出格式:
32

解析:挺簡單的一道題。
   設dp[i]表示第i天所需要的最小成本。
   那麼可以列舉上一次和第i天走的最短路相同的天數j,則dp[i] = max(dp[j - 1] + (i - j + 1) * dis + k),dis是從1到m的最短路。最後輸出答案即可。
   注意dp[0] = -k,因為第一次最短路不需要修改。

程式碼如下:

#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;

const int maxn = 1e5 + 5; //不知道d和e是多少就開了這麼大 QAQ~ 
int n, m, k, e, dis[25], d, mark[105][25], dp[105], bj[25], vis[25];
int nxt[maxn << 1], hed[maxn << 1], to[maxn << 1], val[maxn << 1], cnt;
struct dui{
    int dis, p;
    bool operator<(const dui &a)const{
      return a.dis < dis;
    }
};
priority_queue <dui> heap;

int read(void) {
    char c; while (c = getchar(), c < '0' || c >'9'); int x = c - '0';
    while (c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; return x;
}

void add(int x, int y, int len) {
    nxt[++ cnt] = hed[x]; hed[x] = cnt; to[cnt] = y; val[cnt] = len;
} 

void dijkstra(void) {
    for (int i = 1; i <= m; ++ i) dis[i] = 2e9, vis[i] = 0;
    dis[1] = 0; heap.push((dui){0, 1});
    while (!heap.empty()) {
      dui u = heap.top(); heap.pop();
        if (vis[u.p]) continue;
      vis[u.p] = 1; 
        for (int i = hed[u.p]; i ; i = nxt[i]) {
          int v = to[i];
            if (bj[v]) continue; //如果不能走就換一個點 
            if (dis[v] > dis[u.p] + val[i]) {
              dis[v] = dis[u.p] + val[i];
              heap.push((dui){dis[v], v});
            }
        }
    }
}

int main() {
    n = read(); m = read(); k = read(); e = read();
      for (int i = 1; i <= e; ++ i) {
        int x = read(), y = read(), len = read();
        add(x, y, len); add(y, x, len);
      }
    d = read();
      for (int i = 1; i <= d; ++ i) {
        int p = read(), x = read(), y = read();
          for (int j = x; j <= y; ++ j) mark[j][p] = 1;
      }
    dp[0] = -k; 
      for (int i = 1; i <= n; ++ i) {
        dp[i] = 2e9;
        for (int j = 1; j <= m; ++ j) bj[j] = 0; //bj表示不能走的點 
        for (int j = i; j ; -- j) { //注意要大到小列舉! 
            for (int h = 1; h <= m; ++ h) bj[h] |= mark[j][h];
            dijkstra();
            if (dis[m] == 2e9) break; //如果無法連通,就直接退出 
            dp[i] = min(dp[i], dp[j - 1] + (i - j + 1) * dis[m] + k);
          }
      }
    printf("%d", dp[n]);
    return 0;
}