第五天打卡

本文讲述了如何在给定的设备网络中,通过Kruskal算法构建最大生成树,然后利用LAC算法计算任意两点间的最小稳定路径,以解决网络稳定性查询的问题。作者详细介绍了数据结构、算法实现及其在实际场景中的应用。
摘要由CSDN通过智能技术生成

摆烂了两天,接上回的网络稳定性问题。输入的第一行包含n,m,q三个分别表示设备数、物理连接数和询问数。我们需要找到两个节点之间的稳定性。如果两个节点的路径通过别的设备连接,那么稳定性取最小的,如果两个节点之间不只一条路径,取稳定性最大的。

考虑到时间复杂度不能太大,要用lac最近公共祖先算法。

需要的一些结构体、存储容器等

const const int inf = 1e6 + 5; //最大稳定性
const int N = 1e5 + 1;//最大设备数
const int M = 3e5 + 1;//最大物理连接数
const int K = 17;

struct Edge {
	int u, v, w;  //u为边的起点,v为边的终点,w为边权
	bool operator<(const Edge&e)const //重载小于运算符
	{
		return w > e.w;
	}
};

int n, m, q;
Edge edge[M];
vector<pair<int, int>> g[N];  //图的邻接表
bool visited[N]; //记录节点是否被访问
int p[N + 1]; //记录节点的祖先节点
//祖先数组,代价数组,深度数组
int fa[N + 1][K + 1], cost[N + 1][K + 1], dep[N];

lac算法用于树,先将题中存储的图通过kruskal算法转换为树,kruskal算法为归并边的

//查找祖先
int find(int x)
{
	return x == p[x] ? x : p[x] = find(p[x]);
}

//kruskal算法求图的最大生成树
void kruskal()
{
	//将边权降序排序,使用其中定义的重载运算符
	sort(edge, edge + m);
	for (int i = 0; i < m; i++)
	{
		auto [u,v,w] = edge[i];
		int pu = find(u), pv = find(v);
		if (pu != pv)
		{
			p[pv] = pu;
			g[u].push_back({ v,w });
			g[v].push_back({ u,w });
		}

	}
}

接下来遍历生成树,记录节点的深度,边权,父节点为lac算法做准备。这里fa[root][0]表示root节点的父节点,fa[root][0]表示祖父节点,以此类推。

//dfs遍历生成树,为lca算法做准备
void dfs(int root, int p)
{
	visited[root] = true;
	fa[root][0] = p;
	dep[root] = dep[p] + 1;

	for (int j = 1; j <= K; j++)
	{
		if (fa[root][j - 1] > 0)
		{
			fa[root][j] = fa[fa[root][j - 1]][j - 1];
			cost[root][j] = min(cost[root][j - 1], cost[fa[root][j - 1]][j - 1]);

		}
	}
	//遍历子节点
	for (auto [y, w] : g[root])
	{
		if (y != p)
		{
			cost[y][0] = w;
			dfs(y, root);
		}
	}
}

接下里是lac算法,卡我半天。求两个节点路径的最小权值,两个节点可能不在同一层,但在一支上,意思就是从下层的节点往上找就能找到另一个节点了。一下代码中tmp表示两节点层数差,tmp&1的作用是按位从右到左依次检查tmp(二进制)是否为1,如果是1的话tmp&1的值就是1。假设层数差为2,则tmp=10,第一位是0,if(tmp&1)不执行,第二位是1,执行,然后根据for代码里,tmp>>1,tmp为1,再执行一次,tmp>>1,tmp为0,结束。这时候一个节点上移完成后正好是另一个节点,求出结果。还有一种情况是移动到同一层后发现两个节点不重合,这时候从两个节点的最早祖先依次找,找到第一个不是祖先节点的节点,这个节点的父节点就是这两个节点的最近公共祖先节点。

//lac算法求两个节点路径上的最小权值
int lac(int x, int y)
{
	//使x和y的深度相等
	if (dep[x] > dep[y]) swap(x, y);
	int tmp = dep[y] - dep[x];
	int ans = inf;
	for(int j=0;tmp>0;j++,tmp>>=1)
		if(tmp&1)
		{
			ans = min(ans, cost[y][j]);
			y = fa[y][j];
		}
	//到达同一节点
	if (x == y) return ans;
	//找到第一个不是x,y祖先节点的节点
	for (int j = K; j >= 0; j--)
	{
		if (fa[x][j] != fa[y][j])
		{
			ans = min(ans, min(cost[x][j], cost[y][j]));
			x = fa[x][j], y = fa[y][j]; 
		}
	}
	ans = min(ans,min(cost[x][0], cost[y][0]));
	return ans;
}

main函数如下:

int main()
{
	cin >> n >> m >> q;
	memset(visited, 0, sizeof(visited));
	memset(dep, 0, sizeof(dep));
	memset(cost, 0, sizeof(cost));
	memset(fa, 0, sizeof(fa));

	for (int i = 1; i <= n; i++)
		p[i] = i;
	for(int i=0;i<m;i++)
	{
		int u, v, w;
		cin >> u >> v >> w;
		edge[i] = { u,v,w };
	}
	kruskal();
	for (int i = 1; i <= n; i++)
		if (!visited[i]) dfs(i, 0);
	while (q--)
	{
		int x, y;
		cin >> x >> y;
		if (find(x) != find(y)) //不连通
			cout << "-1\n";
		else
			cout << lac(x, y)<<'\n';
	}
	return 0;
}

以上是我的学习过程,并不是自己的题解,代码来自蓝桥杯官网真题上的大佬题解。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值