【Ybtoj 第18章例4】货车运输【倍增问题】

9 篇文章 0 订阅
8 篇文章 0 订阅

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


解题思路
首先,我们要求出最大生成树。这样做的原因是司机为了让自己能过通过载运更多的货物,显然会选择边权大的两边,由此可以推出我们要求的是最大生成树,去除原来的图中边权比较小的那些边。

得到了这样一个树之后,我们便考虑如何求出两个节点之间最小边权的最大值(即为题中的最大载重),因为这两点之间的路径是唯一的,我们只需要找出这条路径便可以得到答案。我们可以通过LCA来做到这一点。

这一步是LCA一个经典运用:静态树上链的权值查询问题。具体做法是在求出f数组的同时顺便求出一个dis数组, d i s i , j dis_{i,j} disi,j表示从i点出发走了2^j步所经过的边的边权最小值
初始: d i s i , 0 = l e n ( i , f i , 0 ) dis_{i,0}=len(i,f_{i,0}) disi,0=len(i,fi,0)
转移方程: d i s i , j = m i n ( d i s i , j − 1 , d i s f i , j − 1 , j − 1 ) dis_{i,j}=min(dis_{i,j-1},dis_{f_{i,j-1},j-1}) disi,j=min(disi,j1,disfi,j1,j1


代码

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;

const int INF=2147483600;
int n,m,k,q,x,y,fa[50010],head[10010],v[10010],dep[10010],lg[10010],f[10010][25],dis[10010][25];

struct c{
	int x,w,next;
}a[100010];

struct cc{
	int x,y,w;
}lyx[50010];

void add(int x,int y,int w)
{
	++k;
	a[k].x=y;
	a[k].next=head[x];
	a[k].w=w;
	head[x]=k;	
}

bool cmp(cc l,cc r){
	return l.w>r.w;
}

int find(int x){
	if(fa[x]!=x)return fa[x]=find(fa[x]);
	return x;
}

void kruskal(){
	sort(lyx+1,lyx+m+1,cmp);
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		int xx=find(lyx[i].x),yy=find(lyx[i].y);
		if(xx!=yy)
		{
			fa[xx]=yy;
			add(xx,yy,lyx[i].w);
			add(yy,xx,lyx[i].w);
		}
	}
	return;
}

void dfs(int x,int fa){
	v[x]=1;
	dep[x]=dep[fa]+1;
	for(int i=1;i<=lg[dep[x]];i++)
	{
		f[x][i]=f[f[x][i-1]][i-1];
		dis[x][i]=min(dis[x][i-1],dis[f[x][i-1]][i-1]);
	}
	for(int i=head[x];i;i=a[i].next)
	{
		int y=a[i].x;
		if(v[y])continue;
		
			f[y][0]=x;
			dis[y][0]=a[i].w;
			dfs(y,x);
	}
	return;
} 

int lca(int x,int y)
{
	int ans=INF;
	if(find(x)!=find(y))
		return -1;
	if(dep[x]>dep[y])swap(x,y);
	while(dep[y]>dep[x])
	{
		ans=min(ans,dis[y][lg[dep[y]-dep[x]]]);
		y=f[y][lg[dep[y]-dep[x]]];
	}
	if(x==y)return ans;
	for(int i=lg[dep[x]];i>=0;i--)
	{
		if(f[x][i]!=f[y][i])
		{
			ans=min(min(dis[x][i],dis[y][i]),ans);
			x=f[x][i];
			y=f[y][i];
		}
	 } 
	ans=min(ans,min(dis[x][0],dis[y][0]));
	return ans;
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
		scanf("%d%d%d",&lyx[i].x,&lyx[i].y,&lyx[i].w);
	kruskal();
	lg[0]=-1;
	for(int i=1;i<=n;i++)
		lg[i]=lg[i>>1]+1;
	for(int i=1;i<=n;i++)
	{
		if(!v[i])
		{
			dfs(i,0);
			dis[i][0]=INF;
		}
	}
	scanf("%d",&q);
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d",&x,&y);
		printf("%d\n",lca(x,y));
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值