题意:求期望红绿灯时间下,途径若干加油站,经过最多若干个红绿灯,起点与终点的最短路。
思路:每个有红绿灯的节点通过时间怎么算呢?事实上t=red*red/2/(red+green),然后把这个时间附加到节点的出边上。
随后我们建立分层图,第i层表示经过了i个红绿灯时,从源点到该点的最短路径长度。
如果没有油量限制,那么我们直接跑最短路就行了。
注意到加油站很少,于是我们枚举以每个加油站为起点,向其他加油站经过若干个红绿灯的最短路径。若此长度不大于最大油量,那么可以直接转移。
我们用上述信息构造新图,依旧是分层图,可是每一层仅有50个点,且没有油量限制。
一次最短路出解。
Code:
#include <map> #include <queue> #include <string> #include <cstdio> #include <cctype> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef double f2; #define _abs(x) ((x)>0?(x):-(x)) #define N 10010 int n, m, dep, lim, cost; bool isGas[N]; f2 length[N]; map<string, int> M; int id, S, T; int Point_id, lab[11][N]; struct Node { int lab; f2 dis; Node(int _lab = 0, f2 _dis = 0):lab(_lab),dis(_dis){} bool operator < (const Node &B) const { return dis < B.dis; } }; void swap(Node &x, Node &y) { Node tmp = x; x = y; y = tmp; } struct Heap { Node a[110010]; int top; Heap():top(0){} void up(int x) { for(; x != 1; x >>= 1) if (a[x] < a[x >> 1]) swap(a[x], a[x >> 1]); else break; } void down(int x) { int son; for(; (x << 1) <= top; ) { son=(((x<<1)==top)||(a[x<<1]<a[(x<<1)|1]))?(x<<1):((x<<1)|1); if (a[son] < a[x]) { swap(a[son], a[x]); x = son; } else break; } } void insert(const Node &x) { a[++top] = x; up(top); } Node Min() { return a[1]; } void pop() { a[1] = a[top--]; down(1); } }H; queue<int> q; struct Graph { int head[110010], next[450010], end[450010], ind; f2 len[450010], dis[110010]; bool inpath[110010]; void reset() { ind = 0; memset(head, -1, sizeof head); } void addedge(int a, int b, f2 _len) { int q = ind++; end[q] = b; next[q] = head[a]; head[a] = q; len[q] = _len; } void spfa(int S) { register int i, j; for(i = 1; i <= Point_id; ++i) dis[i] = 1e10; dis[S] = 0; memset(inpath, 0, sizeof(inpath)); H.top = 0, H.insert(Node(S, 0)); Node tmp; while(H.top) { tmp = Node(0, 0); while(H.top) { tmp = H.Min(); H.pop(); if (!inpath[tmp.lab] && _abs(tmp.dis - dis[tmp.lab]) <= 1e-6) break; } if (tmp.lab == 0) break; inpath[tmp.lab] = 1; for(j = head[tmp.lab]; j != -1; j = next[j]) if (dis[end[j]] > dis[tmp.lab] + len[j]) { dis[end[j]] = dis[tmp.lab] + len[j]; H.insert(Node(end[j], dis[end[j]])); } } } }G1, G2; void Getaddedge(int a, int b, int len) { int j; if (_abs(length[b]) > 1e-7) for(j = 0; j < dep; ++j) G1.addedge(lab[j][a], lab[j + 1][b], len + length[b]); else for(j = 0; j <= dep; ++j) G1.addedge(lab[j][a], lab[j][b], len); } int Gases[101], top; int main() { scanf("%d%d%d%d%d", &n, &m, &dep, &lim, &cost); register int i, j, k, p; for(i = 0; i <= dep; ++i) for(j = 1; j <= n; ++j) lab[i][j] = ++Point_id; string s, s1, s2; int red, green, num; for(i = 1; i <= n; ++i) { cin >> s >> red >> green; num = M[s]; if (!num) M[s] = num = ++id; if (s == "start") S = num; else if (s == "end") T = num; else if (s.find("gas") != string::npos) isGas[num] = 1; if (red) length[num] = red * red / ((f2)2 * (red + green)); } G1.reset(); G2.reset(); int a, b, len; for(i = 1; i <= m; ++i) { cin >> s1 >> s2 >> s >> len; a = M[s1], b = M[s2]; Getaddedge(a, b, len); Getaddedge(b, a, len); } isGas[S] = isGas[T] = 1; for(i = 1; i <= n; ++i) if (isGas[i]) Gases[++top] = i; for(i = 1; i <= top; ++i) { G1.spfa(lab[0][Gases[i]]); for(j = 1; j <= top; ++j) { if (i == j) continue; f2 add = (Gases[j] != S && Gases[j] != T) ? cost : 0; for(k = 0; k <= dep; ++k) if (G1.dis[lab[k][Gases[j]]] <= lim) for(p = 0; p + k <= dep; ++p) G2.addedge(lab[p][Gases[i]], lab[p + k][Gases[j]], G1.dis[lab[k][Gases[j]]] + add); } } G2.spfa(lab[0][S]); f2 res = 1e10; for(i = 0; i <= dep; ++i) res = min(res, G2.dis[lab[i][T]]); printf("%.3lf", res); return 0; }