摆烂了两天,接上回的网络稳定性问题。输入的第一行包含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;
}
以上是我的学习过程,并不是自己的题解,代码来自蓝桥杯官网真题上的大佬题解。