【NOIP2009】最优贸易 trade
【问题描述】
有n个城市和m条道路,道路有双向道路和单向道路。每个城市的水晶球价格不同,商人阿龙要从城市1走到城市n,城市和道路可以经过多次。阿龙可以在一个城市买入一个水晶球,然后在之后经过的城市卖出(最多交易一次)。求出阿龙最多能够获得的利润。
1≤n≤100000,1≤m≤500000
【分析】
最简单的想法是遍历全图,找到价格的Max和Min,相减得到答案。但是要保证必须先走到Min的城市,再走到Max的城市,所以这样是不行的。我们可以反向构图,分成正着走到n和倒着走到1,令Min[i] 表示从1开始正着走到i的城市中最小的价格,Max[i]表示从n倒着走到i的城市中最大的价格,则ans = max(Max[i] - Min[i])
这样我们可以通过做两遍spfa或者bfs来得到Max和Min的值,规定时间内是可以通过的。
【代码】
PS:我的代码比较戳,由于懒得写链表,直接用vector了,将就看吧。由于是用vector的,可能在某些机子上会计较慢一些,一般还是能过的。
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <vector> using namespace std; int n,m; int a[100001],Max[100001],Min[100001]; int q[1000001],h,t; bool v[100001]; vector<int> edge[100001],edge1[100001]; int main() { freopen("trade.in","r",stdin); freopen("trade.out","w",stdout); scanf("%d %d",&n,&m); for (int i =1;i <= n;i ++) scanf("%d",&a[i]); for (int i = 1;i <= m;i ++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); edge[x].push_back(y); if (z > 1) edge[y].push_back(x); edge1[y].push_back(x); if (z > 1) edge1[x].push_back(y); } memset(Min,63,sizeof(Min)); Min[1] = a[1]; q[1] = 1; v[1] = true; h = 0,t = 1; while (h < t) { if (h != 0) v[q[h]] = false; h ++; for (int i = 0;i < edge[q[h]].size();i++) if (Min[edge[q[h]][i]] > min(a[edge[q[h]][i]],Min[q[h]])) { Min[edge[q[h]][i]] = min(a[edge[q[h]][i]],Min[q[h]]); if (!v[edge[q[h]][i]]) { q[++t] = edge[q[h]][i]; v[edge[q[h]][i]] = true; } } } memset(v,0,sizeof(v)); Max[n] = a[n]; q[1] = n; v[n] = true; h = 0,t = 1; while (h < t) { if (h != 0) v[q[h]] = false; h ++; for (int i = 0;i < edge1[q[h]].size();i++) if (Max[edge1[q[h]][i]] < max(a[edge1[q[h]][i]],Max[q[h]])) { Max[edge1[q[h]][i]] = max(a[edge1[q[h]][i]],Max[q[h]]); if (!v[edge1[q[h]][i]]) { q[++t] = edge1[q[h]][i]; v[edge1[q[h]][i]] = true; } } } int ans = 0; for (int i = 1;i <= n;i ++) { //printf("%d %d\n",Max[i],Min[i]); ans = max(ans,Max[i] - Min[i]); } cout << ans; }