zoukankan      html  css  js  c++  java
  • HDU 5521 Meeting 最短路

    题目链接:

    http://acm.hdu.edu.cn/showproblem.php?pid=5521

    题意;

    给你一副图,n个节点,一个人在1,一个人在n问你在哪个节点相遇花的时间最短。

    题解:

    比较快想到的思路就是起点,终点都跑一遍最短路。

    但是发现边太多,图建不出来。

    重新构造一幅等价的图可以解决这道题:

    为每一个集合建一个新节点,属于某个集合的节点,建一条权值为0的边到代表这个集合的新节点,同时新节点向属于这个集合的节点建一条权值为w(w为这个集合任意两点的距离)的边。

    然后跑两次最短路。

    证明:

    属于集合的点到集合的距离自然为0,而集合到点的距离则表示着从该集合的另一个点到达这个点,所以距离为w。

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #define X first
    #define Y second
    #define mp make_pair
    using namespace std;
    
    const int maxn = 2e5 + 10;
    const int INF = 0x3f3f3f3f;
    
    int n, m;
    vector<pair<int,int> > G[maxn];
    
    int d1[maxn], d2[maxn],dd[maxn];
    int inq[maxn];
    int ans;
    void spfa(int s,int *d) {
        memset(inq, 0, sizeof(inq));
        memset(d, 0x7f, sizeof(int)*maxn);
        queue<int> Q;
        Q.push(s), d[s] = 0, inq[s] = 1;
        while (!Q.empty()) {
            int u = Q.front(); Q.pop();
            inq[u] = 0;
            for (int i = 0; i < G[u].size(); i++) {
                int v = G[u][i].X, w = G[u][i].Y;
                if (d[v] > d[u] + w) {
                    d[v] = d[u] + w;
                    if (!inq[v]) {
                        inq[v] = 1, Q.push(v);
                    }
                }
            }
        }
    }
    
    
    void init() {
        ans = INF;
        for (int i = 0; i < n + m; i++) G[i].clear();
    }
    
    int main() {
        int tc,kase=0;
        scanf("%d", &tc);
        while (tc--) {
            scanf("%d%d", &n, &m);
            init();
            for (int i = 0; i < m; i++) {
                int w, cnt,v;
                scanf("%d%d", &w, &cnt);
                for (int j = 0; j < cnt; j++) {
                    scanf("%d", &v); v--;
                    G[i + n].push_back(mp(v, w));
                    G[v].push_back(mp(i + n, 0));
                }
            }
            spfa(0, d1);
            spfa(n - 1, d2);
            for (int i = 0; i < n; i++) {
                dd[i] = max(d1[i], d2[i]);
                ans = min(ans, dd[i]);
            }
            vector<int> vec;
            for (int i = 0; i < n; i++) if (dd[i] == ans) {
                vec.push_back(i + 1);
            }
            printf("Case #%d: ", ++kase);
            if (ans == INF) {
                printf("Evil John
    ");
            }
            else {
                printf("%d
    ", ans);
                for (int i = 0; i < vec.size() - 1; i++) printf("%d ", vec[i]);
                printf("%d
    ", vec[vec.size() - 1]);
            }
        }
        return 0;
    }

     还有一种思路是用dijkstra做,因为每个集合都只需要做第一次访问(一定是从离起点最近的路径访问进来的,之后访问该集合的都不可能通过集合内的边来松弛了)的松弛操作。

    所以不会超时。

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<queue>
    #include<utility>
    #include<cstring>
    using namespace std;
    
    const int maxn = 1e5 + 10;
    const int INF = 0x3f3f3f3f;
    
    struct Edge {
        int v, d;
        Edge(int v, int d) :v(v), d(d) {}
        Edge() {}
        bool operator < (const Edge& tmp) const {
            return d > tmp.d;
        }
    };
    
    vector<int> G[maxn], S[maxn];
    int val[maxn], n, m;
    
    int d1[maxn], d2[maxn],dd[maxn], used[maxn], vis[maxn];
    void dij(int s, int *d) {
        memset(d, 0x7f, sizeof(int)*maxn);
        memset(used, 0, sizeof(used));
        memset(vis, 0, sizeof(vis));
        
        priority_queue<Edge> pq;
        pq.push(Edge(s, 0)),d[s] = 0;
        while (!pq.empty()) {
            int u = pq.top().v;
            pq.pop();
            if (used[u]) continue;
            used[u] = 1;
            for (int i = 0; i < G[u].size(); i++) {
                int s = G[u][i];
                if (vis[s]) continue;
                vis[s] = 1;
                for (int j = 0; j < S[s].size(); j++) {
                    int v = S[s][j];
                    if (v == u) continue;
                    if (d[v] > d[u] + val[s]) {
                        d[v] = d[u] + val[s];
                        pq.push(Edge(v, d[v]));
                    }
                }
                
            }
        }
    }
    
    void init() {
        for (int i = 0; i < n; i++) G[i].clear();
        for (int i = 0; i < m; i++) S[i].clear();
    }
    
    int main() {
        int tc,kase=0;
        scanf("%d", &tc);
        while (tc--) {
            scanf("%d%d", &n, &m);
            init();
            for (int i = 0; i < m; i++) {
                int cnt, v;
                scanf("%d%d", &val[i], &cnt);
                for (int j = 0; j < cnt; j++) {
                    scanf("%d", &v), v--;
                    G[v].push_back(i);
                    S[i].push_back(v);
                }
            }
            dij(0, d1);
            dij(n - 1, d2);
            int ans = INF;
            for (int i = 0; i < n; i++) {
                dd[i] = max(d1[i], d2[i]);
                ans = min(ans, dd[i]);
            }
            printf("Case #%d: ", ++kase);
            if (ans == INF) {
                printf("Evil John
    ");
            }
            else {
                printf("%d
    ", ans);
                vector<int> a;
                for (int i = 0; i < n; i++) {
                    if (dd[i] == ans) {
                        a.push_back(i + 1);
                    }
                }
                for (int i = 0; i < a.size() - 1; i++) printf("%d ", a[i]);
                printf("%d
    ", a[a.size() - 1]);
            }
        }
        return 0;
    }
  • 相关阅读:
    可恶的Desktop_1.ini 规格严格
    BoundChecker 规格严格
    MySQL NetFlow Analysiser 忘记密码(转载) 规格严格
    Oracle 基本查询 规格严格
    NAN 规格严格
    面试题:为什么java不允许 super.super.xxx 这样的语句?
    国人眼中的Scrum
    NetBeans 时事通讯(刊号 # 1 Apr 02, 2008)
    面试题:为什么java不允许 super.super.xxx 这样的语句?
    快来测试你的脑年龄
  • 原文地址:https://www.cnblogs.com/fenice/p/5568706.html
Copyright © 2011-2022 走看看