zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    Insus Meta Utility
    The 'Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine.
    Insus Binary Utility
    asp.net实现文件下载功能
    Column 'Column Name' does not belong to table Table
    程序已被编译为DLL,怎样去修改程序功能
    如何在Web网站实现搜索功能
    如何把数据流转换为二进制字符串
    Asp.net更新文件夹的文件
    如何显示中文月份
  • 原文地址:https://www.cnblogs.com/Vikyanite/p/14496939.html
Copyright © 2011-2022 走看看