bzoj1003: [ZJOI2006]物流運輸(DP + 最短路)
題目描述:物流公司要把一批貨物從碼頭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; }