Description
(mathrm{FGD}) 想从成都去上海旅游。在旅途中他希望经过一些城市并在那里欣赏风景,品尝风味小吃或者做其他的有趣
的事情。经过这些城市的顺序不是完全随意的,比如说 (mathrm{FGD}) 不希望在刚吃过一顿大餐之后立刻去下一个城市登山,而是希望去另外什么地方喝下午茶。幸运的是, (mathrm{FGD}) 的旅程不是既定的,他可以在某些旅行方案之间进行选择。由于 (mathrm{FGD}) 非常讨厌乘车的颠簸,他希望在满足他的要求的情况下,旅行的距离尽量短,这样他就有足够的精力来欣赏风景或者是泡 (mathrm{MM}) 了_.整个城市交通网络包含 (N) 个城市以及城市与城市之间的双向道路 (M) 条。城市自(1) 至 (N) 依次编号,道路亦然。没有从某个城市直接到它自己的道路,两个城市之间最多只有一条道路直接相连,但可以有多条连接两个城市的路径。任意两条道路如果相遇,则相遇点也必然是这 (N) 个城市之一,在中途,由于修建了立交桥和下穿隧道,道路是不会相交的。每条道路都有一个固定长度。在中途,(mathrm{FGD}) 想要经过 (K(Kle N-2)) 个城市。成都编号为 (1) ,上海编号为 (N) ,而 (mathrm{FGD}) 想要经过的 (N) 个城市编号依次为 (2,3,cdots ,K+1) .举例来说,假设交通网络如下图。
(mathrm{FGD}) 想要经过城市 (2,3,4,5) ,并且在 (2) 停留的时候在 (3) 之前,而在 (4,5) 停留的时候在 (3) 之后。那么最短的旅行方案是 (1-2-4-3-4-5-8) ,总长度为 (19) 。注意 (mathrm{FGD}) 为了从城市 (2) 到城市 (4) 可以路过城市 (3) ,但不在城市 (3) 停留。这样就不违反 (mathrm{FGD}) 的要求了。并且由于 (mathrm{FGD}) 想要走最短的路径,因此这个方案正是 (mathrm{FGD}) 需要的。
Input
第一行包含 (3) 个整数 (N(2le Nle 20000),M(1le Mle 200000),K(0le Kle 20)) ,意义如上所述。
接下来 (M) 行每行包含三个整数 (u,v,w) ,表示 (u) 与 (v) 之间有一条长度为 (w) 的边。
接下来一行包含一个整数 (t) 。
接下来 (t) 行每行两个整数 (u,v) 表示 (u) 停留的时候在 (v) 之前。
Output
只包含一行,包含一个整数,表示最短的旅行距离。
Sample
Sample Input
8 15 4
1 2 3
1 3 4
1 4 4
1 6 2
1 7 3
2 3 6
2 4 2
2 5 2
3 4 3
3 6 3
3 8 6
4 5 2
4 8 6
5 7 4
5 8 6
3
2 3
3 4
3 5
Sample Output
19
Solution
如果有哪个 (mathrm{OJ}) 上有 (mathrm{bzoj}) 上所有的题, (mathrm{bzoj}) 就真该凉了,题面的 (mathrm{Input}) 都不写全,这啥态度。
最短路 + 状压 (mathrm{dp}) 。 (dp[i][j]) 表示已经停留过的集合为 (i) 现在在 (j) 的最小路程。
#include<bits/stdc++.h>
using namespace std;
#define N 20001
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define ll long long
inline int read() {
int x = 0; char ch = getchar(); while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x;
}
int n, m, K, head[N], tot, dp[1 << 21][22], zt[N], dis[22][N];
struct edge { int v, w, next; }e[400001];
inline void add(int u, int v, int w) { e[++tot].v = v, e[tot].w = w, e[tot].next = head[u], head[u] = tot; }
deque<int> q;
bool inq[N];
inline void spfa(int S) {
memset(inq, 0, sizeof inq); q.push_back(S);
memset(dis[S], 127, sizeof dis[S]); dis[S][S] = 0;
ll sum = 0; int cnt = 1;
while (!q.empty()) {
int u = q.front(); q.pop_front();
if ((ll)dis[S][u] * cnt > sum) { q.push_back(u); continue; }
inq[u] = 0, sum -= dis[S][u], cnt--;
for (int i = head[u], v; i; i = e[i].next) if(dis[S][v = e[i].v] > dis[S][u] + e[i].w) {
dis[S][v] = dis[S][u] + e[i].w;
if (!inq[v]) {
inq[v] = 1, sum += dis[S][v], cnt++;
if (q.empty() || dis[S][v] < dis[S][q.front()]) q.push_front(v);
else q.push_back(v);
}
}
}
}
inline void Min(int& a, int b) { if (a ^ -1) a = min(a, b); else a = b; }
int main() {
n = read(), m = read(), K = read();
rep(i, 1, m) { int u = read(), v = read(), w = read(); add(u, v, w), add(v, u, w); }
rep(i, 1, K + 1) spfa(i);
for (int t = read(), u, v, i = 1; i <= t; i++) u = read(), v = read(), zt[v] |= (1 << u - 2);
memset(dp, -1, sizeof dp); dp[0][1] = 0;
rep(i, 0, (1 << K) - 1) rep(j, 1, K + 1) if (dp[i][j] ^ -1) rep(k, 2, K + 1) if ((i & zt[k]) == zt[k])
Min(dp[i | (1 << k - 2)][k], dp[i][j] + dis[j][k]);
int ans = 0x7fffffff;
rep(i, 1, K + 1) if (dp[(1 << K) - 1][i] ^ -1) Min(ans, dp[(1 << K) - 1][i] + dis[i][n]);
printf("%d", ans);
return 0;
}