ABC274E Booster
ABC274E Booster - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
比较板的状压 dp,建议难度评为蓝色,因为本题非常类似,而稍难于难度同为蓝色的 P1171。
本题中我们把城市和加速器都看成点,编号 \(0 \sim n + m - 1\),其中 \(< n\) 的点代表城市,\(\ge n\) 的点代表加速器。
设 \(f(S, T, u)\) 表示经过的城市集合为 \(S\),经过的加速器集合为 \(T\),现在在点 \(u\) 的目前最小总时间花费。
枚举时需保证 \(u \in S \cup T\)。
刷表转移,枚举点 \(v\),如果 \(v\) 是城市且 \(v \not \in S\),那么有转移方程:
\(f(S \cup \{v\}, T, v) = f(S, T, u) + \dfrac{d(u, v)}{2^{\operatorname{popcnt}(T)}}\)
如果点 \(v\) 是加速器且 \(v \not \in T\) 那么有转移方程:
\(f(S, T \cup \{v\}, v) = f(S, T, u) + \dfrac{d(u, v)}{2^{\operatorname{popcnt}(T)}}\)
其中 \(d(u, v)\) 表示 \(u, v\) 两点之间的距离。
边界条件:
如果 \(u\) 是城市: \(f(\{u\}, \varnothing, u) = d(-1, u)\);
如果 \(u\) 是加速器:\(f(\varnothing, \{u\}, u) = d(-1, u)\)。
其中 \(d(-1, u)\) 表示点 \(u\) 到原点的距离。
最后统计答案时,只需统计:
\(\min[f(\{0, 1, \cdots, n - 1\}, T, u) + \dfrac{d(-1, u)}{2^{\operatorname{popcnt}(T)}}, T \subseteq \{n, \cdots, n + m - 1\}, (u < n \lor u \in T)]\)。
不要忘记从 \(v\) 回到原点时可能有加速器加速。
另外:实际代码 \(u < n \lor u \in T\) 其实是不用检验的,因为终点作为加速器,但没有出现在加速器集合的这种 \(f\) 是不合法的,在代码中我们保证它的值是 \(+\infty\),不会被统计入 \(\min\) 中。
代码中 \(S\) 和 \(T\) 需要状压,\(T\) 状压时,点 \(u \ge n\) 在 \(T\) 中的位数是 \(u - n\)。这样可以存储下。
如果预处理 \(\operatorname{popcnt}\),时间复杂度可以达到 \(\mathcal{O}(2^{n + m}(n+m)^2)\)。我没有预处理,所以时间复杂度会再乘一个 \(m\),也可以接受。
/*
* @Author: crab-in-the-northeast
* @Date: 2022-10-22 20:46:15
* @Last Modified by: crab-in-the-northeast
* @Last Modified time: 2022-10-22 21:55:05
*/
#include <bits/stdc++.h>
#define int long long
inline int read() {
int x = 0;
bool flag = true;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-')
flag = false;
ch = getchar();
}
while (isdigit(ch)) {
x = (x << 1) + (x << 3) + ch - '0';
ch = getchar();
}
if(flag)
return x;
return ~(x - 1);
}
const int maxn = 13, maxm = 7;
const int maxs = (1 << 12) + 5, maxt = (1 << 5) + 5;
double f[maxs][maxt][maxn + maxm];
double px[maxn + maxm], py[maxn + maxm];
double dis[maxn + maxm][maxn + maxm];
inline int popcnt(int x) {
int ans = 0;
for (; x; x >>= 1)
if (x & 1)
++ans;
return ans;
}
inline bool gmi(double &a, double b) {
return b < a ? a = b, true : false;
}
signed main() {
int n = read(), m = read();
for (int i = 0; i < n + m; ++i) {
px[i] = (double)read();
py[i] = (double)read();
for (int j = 0; j < i; ++j) {
dis[i][j] = dis[j][i] =
hypot(px[i] - px[j], py[i] - py[j]);
}
}
std :: memset(f, 0x42, sizeof(f));
for (int u = 0; u < n; ++u)
f[1 << u][0][u] = hypot(px[u], py[u]);
for (int u = 0; u < m; ++u)
f[0][1 << u][u + n] = hypot(px[u + n], py[u + n]);
for (int S = 0; S < (1 << n); ++S) {
for (int T = 0; T < (1 << m); ++T) {
for (int u = 0; u < n + m; ++u) {
if (u >= n) {
if ((T & (1 << (u - n))) == 0)
continue;
} else if ((S & (1 << u)) == 0)
continue;
double now = f[S][T][u];
double sp = (1 << (popcnt(T)));
// std :: cout << popcnt(T) << std :: endl;
for (int v = 0; v < n; ++v) {
if (S & (1 << v))
continue;
gmi(f[S | (1 << v)][T][v],
now + dis[u][v] / (sp * 1.0));
}
for (int v = 0; v < m; ++v) {
if (T & (1 << v))
continue;
gmi(f[S][T | (1 << v)][v + n],
now + dis[u][v + n] / (sp * 1.0));
}
}
}
}
double ans = DBL_MAX;
int S = (1 << n) - 1;
for (int T = 0; T < (1 << m); ++T)
for (int u = 0; u < n + m; ++u)
gmi(ans, f[S][T][u] + hypot(px[u], py[u]) / (1 << popcnt(T)));
printf("%.8lf\n", ans);
return 0;
}