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;
}
posted @ 2022-10-24 04:48  dbxxx  阅读(47)  评论(0编辑  收藏  举报