Shortcut (USACO Gold 2019 January)
这个题的基本意思,是在图上加一条边,求能够节省从各个点回到1这个点的牛的总路径长度的最大值。有两个关键条件:
- 路径长度相同的时候,要选字母序靠前的路径。
- 新增的边只有当牛走原来路径能碰上的时候,才能节省距离。
条件2的意思,就是新增边之后用标准方法求最短路的办法是不对的,需要另想办法。条件1的意思,是求最短路的时候需要仔细选择方案。
既然是最短路径,最直观的办法当然还是从Dijkstra开始,我们沿这个往下想,发现是可以找到比较直观的方法的:
- 对每个点,算出最短距离,以及经过这个点的牛的数量,如果shortcut从这里出发(到节点1),则节省的数值可以得到。
- 得到每个点经过牛的数量的办法:每个点保存最短路的第一步走到哪里,如果有几条,就保存目标点编号最小的(这样就保证了最短路径是字母序最前的)。
- 然后对所有牛走一遍最短路,累加牛的数量就得到每个点牛经过的数量,结合最短距离,就可以找到最多节省的距离。
最后的复杂度是。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
int n, m, T;
int c[10005]; // c[i] <= 1e4
vector<pii> adj[10005]; // target, time (<=25000)
int dist[10005];
int nxt[10005]; // first step in shortest path
int cows[10005]; // # of cows walking over i
int main() {
scanf("%d %d %d", &n, &m, &T);
for (int i = 0; i < n; i++)
scanf("%d", &c[i]);
for (int i = 0; i < m; i++) {
int u,v,t;
scanf("%d %d %d", &u, &v, &t);
u--; v--;
adj[u].push_back({v, t});
adj[v].push_back({u, t});
}
priority_queue<pii, vector<pii>, greater<pii>> q;
q.push({0,0});
fill(dist, dist+n, 1e9);
dist[0] = 0;
while (!q.empty()) { // dijkstra
int u = q.top().second, du = q.top().first;
q.pop();
if (du != dist[u]) continue;
for (auto p: adj[u]) {
int v = p.first, dv = p.second;
int d = dist[u] + dv;
if (d < dist[v] || d == dist[v] && u < nxt[v]) {
dist[v] = d;
nxt[v] = u;
q.push({d, v});
}
}
}
for (int i = 1; i < n; i++) { // calc cows over each field
for (int j = i; j != 0; j = nxt[j])
cows[j] += c[i];
}
ll ans = 0;
for (int i = 1; i < n; i++) { // find best field
ans = max(ans, (ll)cows[i] * (dist[i] - T));
}
printf("%lld", ans);
return 0;
}