洛谷 P1967-货车运输

题目描述

A国有 n 座城市,编号从 1n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。

现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。


思路

求出这个图的最大生成树,这样就把问题转化为:树上任意两个点的路径中,边权值的最小值是多少,想到可以搜索,单向搜索比较慢,可以双向搜索,而树上的双向搜索像极了LCA,所以这道题就是最大生成树+最近公共祖先


AC代码

代码又臭又长,当我看到它将这道题AC的时候,我的内心是震惊的。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

const int Maxn = 10005;

struct EDGE1 {
    int u, v, w;
    EDGE1(){}
    EDGE1(int u, int v,int w):u(u), v(v), w(w){}
    bool operator < (const EDGE1 x) const {
        return w > x.w;
    }
} e1[Maxn << 3];

struct EDGE2 {
    int v, w, next;
} e[Maxn << 1];

int tot = 1, father[Maxn], dep[Maxn], fa[Maxn], val[Maxn], head[Maxn];

int Find(int x) {
    return father[x] == x ? x : father[x] = Find(father[x]);
}

void Union(int u, int v) {
    int ru = Find(u);
    int rv = Find(v);
    if (ru != rv) {
        father[ru] = rv;
    }
}

void addedge(int u, int v, int w) {
    e[tot].v = v;
    e[tot].w = w;
    e[tot].next = head[u];
    head[u] = tot++;
}

void Kruskal(int nv, int ne) {
    for (int i = 0; i <= nv; i++) { 
        father[i] = i;
    }
    int k = 0;
    for (int i = 0; i < ne; i++) {
        int u = e1[i].u;
        int v = e1[i].v;
        int w = e1[i].w;
        if (Find(u) != Find(v)) {
            Union(u, v);
            addedge(u, v, w);
            addedge(v, u, w);
            if (++k == nv - 1) {
                break;
            }
        }
    }
}

void bfs(int s) {
    std::queue<int>q;
    q.push(s);
    dep[s] = 1;
    fa[s] = -1;
    for (; !q.empty(); q.pop()) {
        int f = q.front();
        for (int i = head[f]; i; i = e[i].next) {
            int v = e[i].v;
            int w = e[i].w;
            if (!dep[v]) {
                dep[v] = dep[f] + 1;
                val[v] = w;
                fa[v] = f;
                q.push(v);
            }
        }
    }
}

int Lca(int u, int v) {
    if (dep[u] < dep[v]) {
        std::swap(u, v);
    }
    int ans = 0x3f3f3f3f;
    if (dep[u] > dep[v]) {
        ans = std::min(ans, val[u]);
    }
    for (; dep[u] > dep[v];) {
        u = fa[u];
        if (dep[u] > dep[v]) {
            ans = std::min(ans, val[u]);
        }
    }
    // 两个if为了防止到了最近公共祖先的时候还与最近祖先的父节点的距离取依次min
    while (u != v) {
        ans = std::min(ans, val[u]);
        ans = std::min(ans, val[v]);
        u = fa[u];
        v = fa[v];
    }
    return ans;
}

void solve() {
    int nv, ne, nq;
    scanf("%d %d", &nv, &ne);
    int u, v, w;
    for (int i = 0; i < ne; i++) {
        scanf("%d %d %d", &u, &v, &w);
        e1[i] = EDGE1(u, v, w);
    }
    std::sort(e1, e1 + ne);
    Kruskal(nv, ne);
    bfs(1);
    scanf("%d", &nq);
    for (int i = 0; i < nq; i++) {
        scanf("%d %d", &u, &v);
        if (Find(u) != Find(v)) {
            printf("-1\n");
            continue;
        }
        int ans = Lca(u, v);
        printf("%d\n", ans);
    }
}

int main() {
    solve();
    return 0;
}

posted @ 2021-02-18 17:27  牟翔宇  阅读(54)  评论(0编辑  收藏  举报