題目描述
C國有 n 個大城市和 m 條道路,每條道路連接這 n 個城市中的某兩個城市。任意兩個城市之間最多隻有一條道路直接相連。這 m 條道路中有一部分爲單向通行的道路,一部分爲雙向通行的道路,雙向通行的道路在統計條數時也計爲1條。C國幅員遼闊,各地的資源分佈情況各不相同,這就導致了同一種商品在不同城市的價格不一定相同。但是,同一種商品在同一個城市的買入價和賣出價始終是相同的。商人阿龍來到C國旅遊。當他得知“同一種商品在不同城市的價格可能會不同”這一信息之後,便決定在旅遊的同時,利用商品在不同城市中的差價賺一點旅費。設C國 n 個城市的標號從 1~n,阿龍決定從1號城市出發,並最終在 n 號城市結束自己的旅行。在旅遊的過程中,任何城市可以被重複經過多次,但不要求經過所有 n 個城市。阿龍通過這樣的貿易方式賺取旅費:他會選擇一個經過的城市買入他最喜歡的商品——水晶球,並在之後經過的另一個城市賣出這個水晶球,用賺取的差價當做旅費。因爲阿龍主要是來C國旅遊,他決定這個貿易只進行最多一次,當然,在賺不到差價的情況下他就無需進行貿易。現在給出 n 個城市的水晶球價格,m 條道路的信息(每條道路所連接的兩個城市的編號以及該條道路的通行情況)。請你告訴阿龍,他最多能賺取多少旅費。
輸入格式
第一行包含 2 個正整數 n 和 m,中間用一個空格隔開,分別表示城市的數目和道路的數目。
第二行 n 個正整數,每兩個整數之間用一個空格隔開,按標號順序分別表示這 n 個城市的商品價格。
接下來 m 行,每行有 3 個正整數,x,y,z,每兩個整數之間用一個空格隔開。
如果z=1,表示這條道路是城市 x 到城市 y 之間的單向道路;如果z=2,表示這條道路爲城市 x 和城市 y 之間的雙向道路。
輸出格式
一個整數,表示答案。
數據範圍
1≤n≤100000,
1≤m≤500000,
1≤各城市水晶球價格≤100
輸入樣例
5 5
4 3 5 6 1
1 2 1
1 4 1
2 3 2
3 5 1
4 5 2
輸出樣例
5
解題思路
首先本題題意爲:從節點1出發到一個價格最低的節點購買水晶球,再去一個價格最高的節點賣出水晶球,前提是買賣兩個節點使可以到達的。因此可以考慮動態規劃的思路,我們假設買賣由節點k分隔,即在節點1-k購買,在節點k-n賣出,當然也可以不買不賣,那麼有:
- 從 1 走到 i 的過程中,買入水晶球的最低價格 dmin[i];
- 從 i 走到 n 的過程中,賣出水晶球的最高價格 dmax[i];
那麼顯然最大差值爲max(dmax[i] - dmin[i]),接下來考慮算法實現。求dmax[i] 和dmin[i]的過程顯然使用最短路算法,由於這次求的最值是節點的權值最值,數值不累計,因此不能使用Dijkstra算法,於是採用spfa求最值,樸素的實現應該是先求節點1到各個節點最小值,然後分別求節點i到節點n的最大值,這樣就等於執行了n次spfa,這樣複雜度顯然太高,於是考慮反向建圖,即將圖路徑取反,然後求節點n到各個節點的最大距離,這樣節點n到節點i的最大距離,就等於原圖中節點i到節點n的最大距離,這樣只需要執行兩次spfa,節省了很多時間,可參考下邊代碼實現,代碼參考yxc。
代碼
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100010, M = 2000010;
int n, m;
int price[N];
int h[N], rh[N], e[M], ne[M], idx;
int dmin[N], dmax[N];
bool st[N];
void add(int *h, int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
//尋找最小距離,flag來區分正反搜索
void spfa(int *d, int start, int *h, bool flag)
{
queue<int> q;
memset(st, 0, sizeof st);
if (flag) memset(d, 0x3f, sizeof dmin);
q.push(start);
st[start] = true;
d[start] = price[start];
while (q.size())
{
int t = q.front();
q.pop();
st[t] = false;
for (int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
//正搜求最小值,反搜求最大值
if (flag && d[j] > min(d[t], price[j]) || !flag && d[j] < max(d[t], price[j]))
{
if (flag) d[j] = min(d[t], price[j]);
else d[j] = max(d[t], price[j]);
if (!st[j])
{
st[j] = true;
q.push(j);
}
}
}
}
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
memset(rh, -1, sizeof rh);
for (int i = 1; i <= n; i ++ ) scanf("%d", &price[i]);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
//add(rh, b, a)等是反向建圖
add(h, a, b), add(rh, b, a);
if (c == 2) add(h, b, a), add(rh, a, b);
}
//分別進行一次spfa
spfa(dmin, 1, h, true);
spfa(dmax, n, rh, false);
//枚舉求最大差值
int res = 0;
for (int i = 1; i <= n; i ++ ) res = max(res, dmax[i] - dmin[i]);
printf("%d\n", res);
return 0;
}