• Codeforces Round #703 E. Paired Payment


    题意:给定n个结点,m条边的无向连通图。

    走过每条路径需要花费(w <= 50)的时间。

    每次固定走两步,所耗费的时间为(w1 + w2)²。

    输出1到其他所有点的最短时间的最小值。若没有则输出-1。

    这道题目在网上有两种做法。其思想都是相同的。

    参考连接:https://blog.csdn.net/weixin_45697774/article/details/113856213

    https://www.cnblogs.com/irty/p/14416524.html

    第一种:虚点连边+最短路

    对于原图的边我们建立一张新的图。之后将_hash(u, 0)当作不为中间点的点。_hash(u, 1-50)当作作为中间点的点。当一个点连向作为中间点的点时,权值为0。当中间点连向目的点的时候权值为两个权值平方。

    具体的描述应该如下:图片源自第二个博客。

    对于每个点都有作为中间点和不作为中间点的情况。最暴力的思路应该就是在最短路里面去O(n2)去枚举两条边,但是n为2e5。这样的复杂度是我们所无法接受的。

    所幸的是w只有50,那么我们就不枚举两条边。从而通过建虚点的方式获得上一条边的权值。

    这里为了建点方便,就用了一个hash函数。因为权值只有50所以每个点乘上51+w肯定是不会冲突的。

    具体的建图方式如上所述。

    这里放上我的代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 1e7 + 5;
    #define pii pair<int, int>
    vector<pair<int, int> > G[maxn];
    int dis[maxn];
    int n, m;
    
    int _hash(int id, int w) {
        return id * 51 + w;
    }
    
    void _add(int u, int v, int w) {
        G[u].push_back({v, w});
    }
    
    void add(int u, int v, int w) {
        _add(_hash(u, 0), _hash(v, w), 0);
        for (int i = 1; i <= 50; ++ i) {
            _add(_hash(u, i), _hash(v, 0), (i+w)*(i+w));
        }
    }
    
    void dij(int S) {
        priority_queue<pii, vector<pii>, greater<pii> > pq;
        for (int i = 1; i <= maxn-1; ++ i)
            dis[i] = 0x3f3f3f3f;
        pq.push({dis[S] = 0, S});
        while (!pq.empty()) {
            int u = pq.top().second;
            pq.pop();
            for (auto i : G[u]) {
                int v = i.first;
                int w = i.second;
                if (dis[v] > dis[u] + w) {
                    pq.push({dis[v] = dis[u] + w, v});
                }
            }
        }
    }
    
    int main() {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m; ++ i) {
            int u, v; int w;
            scanf("%d%d%d", &u, &v, &w);
            add(u, v, w),  add(v, u, w);
        }
        dij(_hash(1, 0));
        for (int i = 1; i <= n; ++ i) {
            if (i != 1) printf(" ");
            if (dis[_hash(i, 0)] == 0x3f3f3f3f) {
                printf("-1");
            }
            else printf("%d", dis[_hash(i, 0)]);
        }
        puts("");
        return 0;
    }
    View Code

    (注意不需要开long long,开了反而会mle的)

    第二种:多状态最短路

    其实就是利用最短路贪心的思想进行的一个dp转移,开一个dis[点数][边权][是否为中间点]的数组。

    每次就是有两个状态的推移:

    中间点到非中间点:dis[next.id][next.w][0] = min(dis[pre.id][pre.w][1] + 边权)

    非中间点到中间点:dis[next.id][next.w][1] = min(dis[pre.id][pre.w][0])

    之后O(50n)来记录1到其他n个点,上一条边权为1-50且为非中间点的最短距离。即min(dis[1-n][1-50][0])

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 2e5 + 10;
    #define pii pair<int, int>
    vector<pair<int, int> > G[maxn];
    int dis[maxn][51][2];
    int n, m;
    
    void _add(int u, int v, int w) {
        G[u].push_back({v, w});
        G[v].push_back({u, w});
    }
    
    struct node {
        int id, pre, w, state;
        bool operator<(const node& p) const{
            return w > p.w;
        }
    };
    
    void dij() {
        priority_queue<node> pq;
        memset(dis, 0x3f, sizeof(dis));
        pq.push({1, 0, dis[1][0][0] = 0, 0});
        while (!pq.empty()) {
            node now = pq.top();
            pq.pop();
            int x = now.id;
            for (auto i : G[now.id]) {
                int v = i.first, w = i.second;
                int cost = 0;
                if (now.state == 1) {
                    cost = (now.pre + w) * (now.pre + w);
                }
                if (dis[v][w][now.state^1] > dis[now.id][now.pre][now.state] + cost) {
                    dis[v][w][now.state^1] = dis[now.id][now.pre][now.state] + cost;
                    pq.push({v, w, dis[v][w][now.state^1], now.state^1});
                }
            }
        }
    }
    
    int main() {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m; ++ i) {
            int u, v; int w;
            scanf("%d%d%d", &u, &v, &w);
            _add(u, v, w);
        }
        dij();
        for (int i = 1; i <= n; ++ i) {
            int ans = 0x3f3f3f3f;
            for (int j = 0; j <= 50; ++ j) {
                ans = min(dis[i][j][0], ans);
            }
            if (i != 1) printf(" ");
            if (ans == 0x3f3f3f3f) printf("-1");
            else printf("%d", ans);
        }
        puts("");
        return 0;
    }
    View Code
  • 相关阅读:
    【题解】【字符串】【BFS】【Leetcode】Word Ladder
    Python CSV模块处理文件读写
    【题解】【排列组合】【回溯】【Leetcode】Generate Parentheses
    【题解】【链表】【Leetcode】Linked List Cycle II
    【题解】【排列组合】【回溯】【Leetcode】Gray Code
    【题解】【位操作】【Leetcode】Single Number II
    【题解】【数组】【查找】【Leetcode】Search Insert Position
    JAVAWEB的Listener
    Hibernate的检索方式--查询数据的方式
    Hibernate的批量处理和分页技术、投影技术
  • 原文地址:https://www.cnblogs.com/Vikyanite/p/14496939.html
Copyright © 2020-2023  润新知