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\n");
        }
        else {
            printf("%d\n", ans);
            for (int i = 0; i < vec.size() - 1; i++) printf("%d ", vec[i]);
            printf("%d\n", 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\n");
        }
        else {
            printf("%d\n", 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\n", a[a.size() - 1]);
        }
    }
    return 0;
}

 

posted @ 2016-06-07 23:37  fenicnn  阅读(145)  评论(0编辑  收藏  举报