版权声明:本人QQ 249712949 欢迎交流算法(请备注)。 https://blog.csdn.net/coord_/article/details/70233054
这个题借鉴了别人思想自己写的,题目大意是,给定图,保证每两点之间最多有一条直接相连的边,先给出原始图,然后Q
次更新,每次增加某条边的权值,每次更新独立,求出本次的mst值,最后求平均。
所以问题就是根据每次更新以及原始mst求最新mst。两种情况,
- 更新边不是原始mst上的。所以还是原始mst的值。
- 更新边是原始mst上的。因为更新一定是增加,所以这时这条边可能有一条原始mst以外的边来替代。
那么无论原始mst上的边权值怎样增加,只要事先求出原始mst上每条边的最佳替换边(其实就是一个非mst边的权值)就可以了,然后再和更新值比大小。
那么就把非mst上的边从小到大排序,每条边在原始mst上一定形成一个环,那么除该边之外环上所有边的替换边就是他。这个过程用dfs
实现(从非mst边出发,只走mst边,走一个环)。
因为从小到大排序,所以每条mst上的边的替换值要么为初始值(-1
,这时意味替换值不存在),要么只能被赋予一次。
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <utility>
using namespace std;
const int INF = 1e9;
const int MAXN = 1e3 * 3 + 2;
int N, M, Q;
struct Edge
{
int n1, n2, w, flag;
bool operator<(const Edge& e) const
{
return w < e.w;
}
};
vector<Edge> ve;
vector<pair<int, int>> v[MAXN];
int mst;
int pre[MAXN];
int conn;
int rep[MAXN][MAXN];
int counter;
long long total;
void init()
{
ve.clear(); //
mst = 0;
memset(pre, -1, sizeof pre);
conn = 0;
memset(rep, -1, sizeof rep);
for (int i = 0; i < N; i++) v[i].clear();
counter = 0;
total = 0;
}
int f(int x)
{
int f0 = x, f1 = x, t;
for (; pre[f0] >= 0;) f0 = pre[f0];
for (; pre[f1] >= 0;)
{
t = f1;
f1 = pre[f1];
pre[t] = f0;
}
return f0;
}
bool u(int n1, int n2)
{
int f1 = f(n1);
int f2 = f(n2);
if (f1 != f2)
{
conn++;
if (pre[f1] <= pre[f2])
{
pre[f1] += pre[f2];
pre[f2] = f1;
}
else
{
pre[f2] += pre[f1];
pre[f1] = f2;
}
return true;
}
return false;
}
bool dfs(int n, int f, int w, int prev)
{
if (n == f) return true;
for (int i = 0; i < v[n].size(); i++)
{
if (v[n][i].first != prev) //
{
if (dfs(v[n][i].first, f, w, n))
{
if (rep[n][v[n][i].first] == -1)
{
rep[n][v[n][i].first] = rep[v[n][i].first][n] = w;
counter++;
}
return true;
}
}
}
return false;
}
int main()
{
int a, b, c;
for (; ~scanf("%d%d", &N, &M) && N + M;)
{
init();
for (int i = 0; i < M; i++)
{
scanf("%d%d%d", &a, &b, &c);
ve.push_back({ a,b,c,0 });
}
sort(ve.begin(), ve.end());
for (int i = 0; i < ve.size(); i++)
{
if (u(ve[i].n1, ve[i].n2))
{
mst += ve[i].w;
ve[i].flag = 1;
v[ve[i].n1].push_back(make_pair(ve[i].n2, ve[i].w));
v[ve[i].n2].push_back(make_pair(ve[i].n1, ve[i].w)); // 邻接表只加mst上的边
}
if (conn == N - 1) break;
}
for (int i = 0; i < ve.size(); i++)
{
if (!ve[i].flag)
{
dfs(ve[i].n1, ve[i].n2, ve[i].w, -1);
if (counter == N - 1) break; // 可以提前退出,然而这里可能到最后counter还没到N-1 !
} // 也就是说 可能不能 为所有mst上的边匹配最佳替换边!
}
scanf("%d", &Q);
bool flag;
int old;
for (int i = 0; i < Q; i++)
{
flag = 0;
scanf("%d%d%d", &a, &b, &c);
for (int j = 0; j < v[a].size(); j++)
{
if (v[a][j].first == b)
{
flag = 1;
old = v[a][j].second;
break;
}
}
if (flag) // 所谓最佳替换边也不一定要用,还要和原mst上的边的最新值比较一下看谁更小
{
if (rep[a][b] == -1) rep[a][b] = INF; // 重点来了!! 等于-1说明该边没有替换边!!!
total += mst - old + min(c, rep[a][b]);
}
else total += mst;
}
printf("%.4f\n", (double)total / (double)Q);
}
return 0;
}