Q3.5.1.5. 旅游景点's 题解
前言
关键点是状压 DP 。通过数据范围容易联想到,且当找不到一个合适的贪心策略时,就应该往 DP 上思考。
正文
形式化题面:
现在有一个 \(n\) 个点 \(m\) 条边的双向连通图。给出一个 \(k\) ,表示我需要激活的点为 \(2\) 到点 \(k+1\) 。再给出 \(T\) 个限制,格式为 \(u~v\) ,表示在激活 \(u\) 之前一定要先激活 \(v\) 。如果一个点没有前置要求点或是前置要求点都已激活,则经过这个点时这个点就被激活。题目保证有解。
数据范围: \(2 \le n \le 2 \times 10^4,1 \le m \le 2 \times 10^5,0 \le k \le 20,0 \le T \le K^2\) ,对于限制的 \(u~v\) ,满足 \(2 \le u,v \le k+1\)
模型判断
观察数据范围,看到 \(k\) 的取值范围,可以联想到状压 DP 或者 dfs 搜索。继续观察,我们通过限制顺序不难联想到拓扑排序,但这就把问题带来了:拓扑序靠后的不一定在拓扑序靠前的后面激活。所以,我们无法用一个贪心策略完成这道题。OK,buff叠满,最终决定使用状压 DP 完成。
算法设计
设 \(f_{S,ed}\) 表示为激活了的点集为 \(S\) ,且最后一次激活的点为 \(ed\) 。通过预知的能力,我们不难察觉到转移需要最短路计算,所以我们再预处理一个数组 \(dist_{u,v}\) 表示为点 \(u\) 到点 \(v\) 的最短路径。由于我们只在意 \(k\) 个点与点 \(1\) 到其他点的最短路,所以预处理最短路的时间复杂度为 \(\Theta(KM \log M)\) 。又通过简单的思考,发现对于一个局面的合法性,我们需要判断,而判断需要“我”影响的点集,所以预处理出 \(lim_u\) ,表示为被点 \(u\) 限制的点集。
边界: \(f_{\{a\},a} = dist_{1,a} (2 \le a \le k+1)\)
状态转移方程(注意:下式在满足条件 \(lim_{ed}~\cap~S = \emptyset\) 的情况下有效):
答案: \(\min_{ed \in \{2,3...,k+1\}} (f_{\{2,3...,k+1\},ed} + dist_{ed,n})\)
时间复杂度:\(\Theta(2^MM + KM \log M)\)
代码
#include <cstdio>
#include <algorithm>
#include <string>
#include <queue>
#include <cstring>
#pragma GCC optimize(3)
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int ui;
const int MAXSZ = 1 << 20;
char ch, buf[MAXSZ], cuf[MAXSZ], *p1, *p2;
int C;
#define ge() (p1 == p2 && (p2 = buf + fread(p1 = buf, 1, MAXSZ, stdin), p1 == p2) ? EOF : *p1++)
#define pu(c) (C ^ MAXSZ ? cuf[C ++] = c : (fwrite(cuf, 1, MAXSZ, stdout), cuf[(C = 0) ++] = c))
#define Flush() (fwrite(cuf, 1, C, stdout), C = 0)
#define line() pu('\n')
#define space() pu(' ')
#define mset(a, b) memset(a, b, sizeof a)
#define I inline
#define int long long
template <typename T_T>
I void read(T_T &x) {
x = 0;
bool flag = false;
while (ch < '0' || '9' < ch) {
if (ch == EOF) {
Flush();
exit(0);
}
ch = ge(), flag |= (ch == '-');
}
while ('0' <= ch && ch <= '9') {
x = x * 10 + (ch ^ 48);
ch = ge();
}
if (flag) x = -x;
}
bool Ccheck(char cha) {
// 此处填判断条件
}
bool Scheck(char cha) {
// 此处填判断条件
}
I void cread(char &x) {
while (!Ccheck(ch)) {
if (ch == EOF) {
Flush();
exit(0);
}
ch = ge();
}
x = ch, ch = ge();
}
I void sread(std::string &str) {
str = "";
while (!Scheck(ch)) {
if (ch == EOF) {
Flush();
exit(0);
}
ch = ge();
}
while (Scheck(ch)) {
str += ch;
ch = ge();
}
}
template <typename T_T>
I void write(T_T x) {
if (x < 0) {
pu('-');
x = -x;
}
if (x > 9) write(x / 10);
pu(x % 10 | 48);
}
template <typename T_T>
I void Write(T_T x, bool flag = true) {
write(x);
if (flag) line();
else space();
}
template <typename T_T>
I T_T max(T_T a, T_T b) { return a > b ? a : b; }
template <typename T_T>
I T_T min(T_T a, T_T b) { return a < b ? a : b; }
template <typename T_T>
I void fmax(T_T &a, T_T b) { a = a > b ? a : b; }
template <typename T_T>
I void fmin(T_T &a, T_T b) { a = a < b ? a : b; }
const int M = 20, M2 = 1 << 20, N = 2e4 + 5, E = 2e5 + 5, INF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, lim[M + 2], head[N], dist[M + 6][N], edgem, T, rec[M2][M + 2];
struct Edge {
int to, w, next;
}edge[E << 1];
void add(int u, int v, int w) {
edge[++ edgem] = {v, w, head[u]};
head[u] = edgem;
}
struct pq {
int u, dist;
bool operator<(const pq &other) const {
return dist > other.dist;
}
};
std::priority_queue <pq> q;
void Dijkstra(int *dist) {
while (!q.empty()) {
pq t = q.top(); q.pop();
int u = t.u, d = t.dist;
if (d != dist[u]) continue ;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to, w = edge[i].w;
if (dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
q.emplace((pq){v, dist[v]});
}
}
}
}
int f(int S, int ed) {
if (rec[S][ed] != INF) return rec[S][ed];
int T = S ^ (1 << ed >> 2);
if (!T) return dist[1][ed];
if (T & lim[ed]) return INF;
for (int nw = 2; nw <= k + 1; ++nw) {
if (!(T & (1 << nw >> 2))) continue ;
fmin(rec[S][ed], f(T, nw) + dist[nw][ed]);
}
return rec[S][ed];
}
signed main() {
// freopen("Ryan.in", "r", stdin);
// freopen("Ryan.out", "w", stdout);
read(n), read(m), read(k);
while (m --) {
int u, v, w; read(u), read(v), read(w);
add(u, v, w);
add(v, u, w);
}
read(T);
while (T --) {
int u, v; read(u), read(v);
lim[u] |= (1 << v >> 2);
}
for (int i = 2; i <= k + 1; ++i) {
mset(dist[i], 0x3f);
dist[i][i] = 0;
q.emplace((pq){i, 0});
Dijkstra(dist[i]);
}
mset(dist[1], 0x3f);
dist[1][1] = 0;
q.emplace((pq){1, 0});
Dijkstra(dist[1]);
if (!k) {
Write(dist[1][n]);
Flush();
return 0;
}
// for (int i = 1; i <= k; ++i)
// for (int j = 1; j <= n; ++j)
// Write(dist[i][j], j == n);
mset(rec, 0x3f);
int ans = INF, S = (1 << k) - 1;
for (int i = 2; i <= k + 1; i++)
fmin(ans, f(S, i) + dist[i][n]);
Write(ans);
Flush();
fclose(stdin); fclose(stdout);
return 0;
}

浙公网安备 33010602011771号