bzoj1097 [POI2007]旅游景点atr
Description
\(\mathrm{FGD}\) 想从成都去上海旅游。在旅途中他希望经过一些城市并在那里欣赏风景,品尝风味小吃或者做其他的有趣
的事情。经过这些城市的顺序不是完全随意的,比如说 \(\mathrm{FGD}\) 不希望在刚吃过一顿大餐之后立刻去下一个城市登山,而是希望去另外什么地方喝下午茶。幸运的是, \(\mathrm{FGD}\) 的旅程不是既定的,他可以在某些旅行方案之间进行选择。由于 \(\mathrm{FGD}\) 非常讨厌乘车的颠簸,他希望在满足他的要求的情况下,旅行的距离尽量短,这样他就有足够的精力来欣赏风景或者是泡 \(\mathrm{MM}\) 了_.整个城市交通网络包含 \(N\) 个城市以及城市与城市之间的双向道路 \(M\) 条。城市自\(1\) 至 \(N\) 依次编号,道路亦然。没有从某个城市直接到它自己的道路,两个城市之间最多只有一条道路直接相连,但可以有多条连接两个城市的路径。任意两条道路如果相遇,则相遇点也必然是这 \(N\) 个城市之一,在中途,由于修建了立交桥和下穿隧道,道路是不会相交的。每条道路都有一个固定长度。在中途,\(\mathrm{FGD}\) 想要经过 \(K(K\le 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(2\le N\le 20000),M(1\le M\le 200000),K(0\le K\le 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;
}