zoukankan      html  css  js  c++  java
  • 「GXOI / GZOI2019」旅行者

    传送门

    这题给出两种方法,只给出前一种方法的代码,因为后一种代码可能跑不过(后面会讲)

    方法1

    我们不难发现最优情况下的路径,一定只在起点和终点两个位置是关键点,中间不可能有别的关键点,不然就可以只取一半,就会更优。

    那么我们就可以建正反两张图,跑两次多源 ( ext{Dijkstra}),记 (f_u) 为从关键点走到 (u) 的最短路,(rf_u) 记录是哪个点,(g_u) 表示从 (u) 点走到某一个关键点的最短路,(rg_u) 记录是哪个点。

    那么我们枚举每一条边 ((u, v, w)),如果 (rf_u e rg_v) ,就可以用 (f_u + w + g_v) 更新答案,如果 (rf_u = rg_v),可以证明这样一定不会是最优解。

    方法2

    新建超级源汇点 (s, t),把所有关键点分为 (A, B) 两个点集,钦定用一条路径 (s o a o b o t)(s o b o a o t) 更新答案,其中 (a in A, b in B)

    考虑怎么分组以覆盖所有的组合情况,我们发现最优解中的 (a, b) 一定是两个不同的点,那么我们把这些点按照其二进制表示的某一位的值来分组,总共分 (log) 次,不难发现这样一定可以覆盖所有可能的解。

    分完组之后就直接把 (s)(A) 中的点连边权为 (0) 的边,(B) 中的点向 (t) 连边权为 (0) 的边,跑一边最短路就可以求出当前分组下 (s o a o b o t) 类型的最优解,另一种类型同理。

    这样复杂度是 (n log^2 n) 的,理论上来说可以过,但是由于每次都要删边连边,所以操作不当就可能会 ( ext{TLE}) ,这也是为什么不用这种方法的原因(((

    方法1参考代码:

    #include <cstring>
    #include <cstdio>
    #include <queue>
    using namespace std;
    
    template < class T > void read(T& s) {
        s = 0; int f = 0; char c = getchar();
        while ('0' > c || c > '9') f |= c == '-', c = getchar();
        while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
        s = f ? -s : s;
    }
    
    typedef long long LL;
    const int _ = 1e5 + 5, __ = 5e5 + 5;
    
    int tot, head[_]; struct Edge { int v, w, nxt; } edge[__];
    void Add_edge(int u, int v, int w) { edge[++tot] = (Edge) { v, w, head[u] }, head[u] = tot; }
    
    int n, m, k, a[_], vis[_], r[2][_]; LL dis[2][_];
    struct node { int u, v, w; } o[__];
    priority_queue < pair < LL, int > > Q;
    
    void Dijkstra(int x) {
        memset(dis[x], 0x3f, sizeof dis[x]);
        memset(r[x], 0, sizeof r[x]);
        memset(vis, 0, sizeof vis);
        for (int i = 1; i <= k; ++i)
            dis[x][a[i]] = 0, r[x][a[i]] = a[i], Q.push(make_pair(0, a[i]));
        while (!Q.empty()) {
            int u = Q.top().second; Q.pop();
            if (vis[u]) continue ; vis[u] = 1;
            for (int i = head[u]; i; i = edge[i].nxt) {
                int v = edge[i].v, w = edge[i].w;
                if (dis[x][v] > dis[x][u] + w)
                    dis[x][v] = dis[x][u] + w, r[x][v] = r[x][u], Q.push(make_pair(-dis[x][v], v));
            }
        }
    }
    
    void solve() {
        read(n), read(m), read(k);
        for (int u, v, w, i = 1; i <= m; ++i)
            read(u), read(v), read(w), o[i] = (node) { u, v, w };
        for (int i = 1; i <= k; ++i) read(a[i]);
        memset(head, tot = 0, sizeof head);
        for (int i = 1; i <= m; ++i) Add_edge(o[i].u, o[i].v, o[i].w);
        Dijkstra(0);
        memset(head, tot = 0, sizeof head);
        for (int i = 1; i <= m; ++i) Add_edge(o[i].v, o[i].u, o[i].w);
        Dijkstra(1);
        LL ans = 1e18;
        for (int i = 1; i <= m; ++i) {
            int u = o[i].u, v = o[i].v, w = o[i].w;
            if (r[0][u] && r[1][v] && r[0][u] != r[1][v] && u != v)
                ans = min(ans, dis[0][u] + w + dis[1][v]);
        }
        printf("%lld
    ", ans);
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
        freopen("cpp.in", "r", stdin), freopen("cpp.out", "w", stdout);
    #endif
        int T; read(T);
        while (T--) solve();
        return 0;
    }
    
  • 相关阅读:
    HTML基础
    JVM内存和JVM调优(五)--分代垃圾回收详述
    JVM内存和JVM调优(四)--如何区分垃圾
    JVM内存和JVM调优(三)--基本GC垃圾回收算法
    JVM内存和JVM调优(二)--引用类型
    JVM内存和JVM调优(一)--堆栈概念
    isAssignableFrom和instanceof
    spring学习(三十九)--自定义注解
    SPRING学习(三十八)--SPRING集成MYBATIS之数据库连接池和多种数据源配置方式(三)
    SPRING学习(三十七)--SPRING集成MYBATIS(二)
  • 原文地址:https://www.cnblogs.com/zsbzsb/p/13052266.html
Copyright © 2011-2022 走看看