UVA-11354-Bond(最小瓶颈路=最小生成树+LCA)

题目:

                  

题意:给定q个查询,每个查询包括u,求u到v的路上所有走过的边中的最大值最小,也就是最小瓶颈路

题意分析:因为是个m条边的图,并且最小瓶颈路包括在最小生成树中(因为最小生成树上任意两个点能互相到,并且最短的边都在上面)有两种情况:

1.数据比较小的时候,我们可以先建立一棵最小生成树,然后在树上dfs

2.优化:建立完最小生成树(注意不要用路径压缩,因为会破坏树的结构,导致建图出现问题)后,对于u,v点求LCA,maxcost[i][j]表示从节点 i 到 i的2^j 的父节点中所经历的最大路径,就是LCA模板上加一个更新maxcost,别的都一摸一样,其实还感觉很难,为了这个题目折腾一个上午不敢自己动手,最后看代码发现只要在LCA的基础上加个东西就好了,所以说以后还是不能怂啊,要多写代码,多练本事~

maxcost[i][j]=max(maxcost[i][j-1],maxcost[fa[i][j-1]][j-1])

然后每次查询求一遍LCA,再返回maxcost就好了,具体看代码

代码:

/*其实Kruskal生成最小生成树后,后面都是LCA的模板,只是在更新父节点的时候加上一个更新
maxcost[i][j]=max(maxcost[i][j-1],maxcost[fa[i][j-1]][j-1]),别的都没区别*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<cmath>
#include<vector>
#include<set>
#include<stack>
#define N 50005
#define M 100005
using namespace std;
int n,m,c,q;
int head[N],fa[N][23],deg[N],maxcost[N][23],f[N];
struct ljh
{
    int x,y,z;
}a[M];
struct xqy
{
    int next,to,w;
}e[M];
bool cmp(ljh u,ljh v)
{
    return u.z<v.z;
}
int find(int x)
{
    if(x!=f[x])return f[x]=find(f[x]);
    return x;
}
inline void add(int x,int y,int z)
{
    e[c].next=head[x];
    e[c].w=z;
    e[c].to=y;
    head[x]=c++;
}
void kruskal()
{
    c=0;
    int tot=0;
    for(int i=1;i<=n;i++)f[i]=i;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=m;i++)
    {
        int u=find(a[i].x);
        int v=find(a[i].y);
        if(u!=v)
        {
            f[u]=v;
            tot++;
            add(a[i].x,a[i].y,a[i].z);
            add(a[i].y,a[i].x,a[i].z);
        }
        if(tot==n-1)break;
    }
    return;
}
void dfs(int u,int pre)
{
    deg[u]=deg[pre]+1;
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        if(!deg[e[i].to])
        {
            fa[e[i].to][0]=u;
            maxcost[e[i].to][0]=e[i].w;
            dfs(e[i].to,u);
        }
    }
    return;
}
int LCA(int x,int y)
{
    int ans=-1;
    if(deg[x]<deg[y])swap(x,y);
    for(int i=0;i<22;i++)
        if((deg[x]-deg[y])&(1<<i))
        {
            ans=max(ans,maxcost[x][i]);
            x=fa[x][i];
        }
    if(x==y)return ans;
    for(int i=22;i>=0;i--)
    {
        if(fa[x][i]!=fa[y][i])
        {
            ans=max(ans,maxcost[x][i]);
            ans=max(ans,maxcost[y][i]);
            x=fa[x][i];
            y=fa[y][i];
        }
    }
    ans=max(ans,maxcost[x][0]);
    ans=max(ans,maxcost[y][0]);
    return ans;
}
int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif
    int T=0;
    while(~scanf("%d%d",&n,&m))
    {
        if(T!=0)printf("\n");
        T++;
        for(int i=1;i<=m;i++)scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
        sort(a+1,a+m+1,cmp);
        kruskal();
        memset(deg,0,sizeof(deg));
        memset(fa,-1,sizeof(fa));
        memset(maxcost,0,sizeof(maxcost));
        dfs(1,1);
        for(int j=1;(1<<j)<=n;j++)
            for(int i=1;i<=n;i++)
                if(fa[i][j-1]!=-1)
                {
                    fa[i][j]=fa[fa[i][j-1]][j-1];
                    maxcost[i][j]=max(maxcost[i][j-1],maxcost[fa[i][j-1]][j-1]);
                }
        scanf("%d",&q);
        while(q--)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            printf("%d\n",LCA(x,y));
        }
    }
}

还有一种方法就是按秩合并,感觉代码复杂度较高,因为他是在不进行路径压缩的基础上,一个节点一个节点的向上访问的,也是类似LCA的方法,关于按秩合并与路径压缩都是并查集的优化,具体可看传送门

代码:

#include <bits/stdc++.h>
using namespace std;
#define INF 1000000009
#define N 50010
int n,m,q,x,y;
int fa[N],Rank[N],edge[N],c[N];
struct node
{
	int x,y,z;
}a[100010];
int cmp(node a, node b)
{
	return a.z < b.z;
}
int Find(int x)
{
	return fa[x] == x ? x:Find(fa[x]);//不进行路径压缩
}
void Kruscal()
{
	int s = 0,f1,f2;
	for (int i=1;i<=n;i++) fa[i] = i, Rank[i] = 1;
	memset(edge, 0, sizeof(edge));
	for (int i=1;i<=m;i++)
	{
		f1 = Find(a[i].x);
		f2 = Find(a[i].y);
		if (f1 != f2)
		{
			if (Rank[f1] < Rank[f2])
			{
				fa[f1] = f2;
                /*当两个秩不相等时,我们使具有高秩的根成为具有较低秩的根的父结点,
                但秩本身保持不变。
                当两个秩相同时,任选一个根作为父结点,并增加其秩的值路径压缩。*/
				edge[f1] = a[i].z;
				Rank[f2] = max(Rank[f2], Rank[f1]+1);
			}
			else 
			{
				fa[f2] = f1;
				edge[f2] = a[i].z;
				Rank[f1] = max(Rank[f1], Rank[f2]+1);
			}
			s++;
		}
		if (s == n-1) break;
	}
}
int query(int x, int y)
{
	for (int i=1;i<=n;i++) c[i] = -1;//表示从x往上走走到i的时候路径的最大值
	int tmp = 0, ans = 0;
	while (1)
	{
		c[x] = tmp;
		if (fa[x] == x) break;
		tmp = max(tmp, edge[x]);
		x = fa[x];
	}
	while (1)
	{
		if (c[y] >= 0){ans = max(ans,c[y]);break;}//走到了x和y的LCA的位置了
		if (fa[y] == y) break;
		ans = max(ans, edge[y]);
		y = fa[y];
 
	}
	return ans;
}
int main()
{
	bool flag = 0;
	while (~scanf("%d%d", &n, &m))
	{
		if (flag) printf("\n");
		for (int i=1;i<=m;i++)
			scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].z);
		sort(a+1,a+m+1,cmp);
		Kruscal();
		scanf("%d", &q);
		for (int i=1;i<=q;i++)
		{
			scanf("%d%d", &x, &y);
			printf("%d\n", query(x,y));
		}
		flag = 1;
	}
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值