Bond UVA - 11354 (按秩合并优化的并查集)

题意:

链接:https://vjudge.net/problem/UVA-11354

给你 n 个点,m 条边无向边,每条边告诉起点和终点还有权值,q次询问,每次询问从 s 点到 t 点的路径上的最小的最大权值(最大权值最小化)

解题思路:

首先如果把这个无向图建成一个最小生成树,那么在最小生成树的任意两点有仅有一条路,并且也使得最大权值最小化了,但是每次都要花费 o(n) 的时间搜索,时间复杂度为 : Q * N 爆掉了,所以可以用按秩合并(每个节点秩最大为 floor(lgn) )的并查集来做,时间复杂度为 Q * lg(n) ,经过秩优化的并查集,查询的时间为 lg(n) ,具体证明算法导论 P332 左上角。(秩可以理解为就是树的深度,一个孤立节点的秩为 0)
注 : 为了保证留住边的路经,固然不可以路径压缩

AC代码:

#include<bits/stdc++.h>
#define up(i, x, y) for(int i = x; i <= y; i++)
#define down(i, x, y) for(int i = x; i >= y; i--)
#define bug printf("*********\n")
#define debug(x) cout<<#x"=["<<x<<"]" <<endl
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int maxn = 1e5 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
using namespace std;

int n, m;
int pre[maxn], cost[maxn], dep[maxn], vis[maxn];
struct node
{
    int u, v, w;
}a[maxn];

int find_(int x)
{
    while(x != pre[x]) x = pre[x];
    return x;
}

int cmp(node a, node b)
{
    return a.w < b.w;
}

void build(int x, int y, int w)
{
    int fx = find_(x), fy = find_(y);
    if(fx == fy) return ;
    if(dep[fx] < fy)  // 按秩合并,秩小的连到秩大的
    {
        pre[fx] = fy;
        cost[fx] = w;
    }
    else if(dep[fx] > fy)
    {
        pre[fy] = fx;
        cost[fy] = w;
    }
    else
    {
        pre[fx] = fy;  // 如果秩相同,随便连
        dep[fy]++;  // 记录每个节点秩的大小
        cost[fx] = w;
    }
    return ;
}

void init()
{
    for(int i = 1; i <= n; i++)
    {
        cost[i] = 0;
        dep[i] = 0;
        pre[i] = i;
    }
    for(int i = 1; i <= m; i++)
    {
        scanf("%d %d %d", &a[i].u, &a[i].v, &a[i].w);
    }
    sort(a + 1, a + 1 + m, cmp);
    for(int i = 1; i <= m; i++)
    {
        build(a[i].u, a[i].v, a[i].w);
    }
}

int query(int s, int t)
{
    int ans, cur;
    int boos = find_(s);
    int cost_s = 0, cost_t = 0;
    cur = s;
    vis[cur] = 0;
    while(cur != boos) // 先从s点找到根节点
    {
        cost_s = max(cost_s, cost[cur]);
        cur = pre[cur];
        vis[cur] = cost_s;
    }
    cur = t;
    while(1)  // 找最进公共祖先
    {
        if(vis[cur] > -1)
        {
            ans = max(cost_t, vis[cur]);
            break;
        }
        cost_t = max(cost_t, cost[cur]);
        cur = pre[cur];
    }
    cur = s;
    while(1)  // 动态清 vis 数组,如果每次计算使用 memset 会T
    {
        vis[cur] = -1;
        if(cur == boos) break;
        cur = pre[cur];
    }
    return ans;
}

int main()
{
    int case_ = 0;
    memset(vis, -1, sizeof(vis));
    while(~scanf("%d %d", &n, &m))
    {
        init();
        if(case_) puts("");
        case_++;
        int q; scanf("%d", &q); while(q--)
        {
            int x, y; scanf("%d %d", &x, &y);
            printf("%d\n", query(x, y));
        }
    }
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值