HDU4126 - 最小生成树 + DP
题目链接
题目大意
给你一个图,有q次询问,每次询问如果替换一条边后最小生成树的大小是多少,求q次询问的平均值。(要替换的边的权一定不小于原来边权,替换只在当前次询问有效)
数据范围
多组输入T<=20,,边权。
解题思路
首先找到一颗最小生成树,之后当要替换的边不在最小生成树里的话,当前的最小生成树就直接是最小生成树的值。如果要替换的边在最小生成树里面的话,比如(u,v)边是要替换的边,将(u,v)删掉之后就会形成两个子树,就需要找出一条能连接这两个子树最小的那条边,那我们就可以实现处理出最小生成树上每条边的最小替换值。假如要替换的边一端是点p,那么假如p在u子树中,只需要在v子树中找到一个连接p的一条最小的边。那么做n次dfs,每次dfs处理以i点为根,且以i作为要替换的一个端点时(u,v)边的最小替换值,这样就处理出了最小生成树中所有边的最小替换值。而dfs时,假如从点i遍历到了u,u遍历v时,要找(u,v)的最小替换边,只需要找到i与v子树中所有边的最小值。但如果要找的是(i,u)边的最小替换边,那就不能包括点u了,因为u和i是直接相连的,是要被替换的,所以只能是i连u子树且除了u点之外的点的最小边。
AC代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
const int INF = 1e9 + 7;
const int maxn = 3000;
int n, m, q;
vector<int>E[maxn + 5];
LL sum;
bool vis[maxn + 5];
int dis[maxn + 5], dp[maxn + 5][maxn + 5], a[maxn + 5][maxn + 5];
int pre[maxn + 5];
void Prim(int x) {
sum = 0LL;
for(int i = 0; i < n; i++) dis[i] = a[x][i], pre[i] = x, vis[i] = 0, E[i].clear();
vis[0] = 1, pre[0] = -1, dis[0] = INF;
for(int i = 1; i < n; i++) {
int k = 0;
for(int j = 0; j < n; j++) {
if(!vis[j] && dis[k] > dis[j])k = j;
}
vis[k] = 1;
if(pre[k] != -1) {//建最小生成树
E[k].push_back(pre[k]);
E[pre[k]].push_back(k);
}
sum += (LL)dis[k];
for(int j = 0; j < n; j++) {
if(!vis[j] && dis[j] > a[k][j]) {
dis[j] = a[k][j];
pre[j] = k;
}
}
}
}
int Dfs(int u, int fa, int p) {
int res = INF;
for(int i = 0; i < E[u].size(); i++) {
int v = E[u][i];
if(v != fa) {
int tmp = Dfs(v, u, p);
res = min(tmp, res);
dp[u][v] = dp[v][u] = min(dp[u][v], tmp);
}
}
if(p != fa)res = min(res, a[p][u]);
return res;
}
int main() {
while(~scanf("%d%d", &n, &m)) {
if(n == 0 && m == 0)break;
for(int i = 0; i < n; i++)for(int j = 0; j < n; j++)a[i][j] = dp[i][j] = INF;
for(int i = 1; i <= m; i++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
a[u][v] = a[v][u] = w;
}
Prim(0);
for(int i = 0; i < n; i++) Dfs(i, -1, i);//处理所有最小生成树上边的最小替换值
scanf("%d", &q);
LL res = 0LL;
for(int i = 1; i <= q; i++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
if(pre[u] != v && pre[v] != u) res += sum;
else res += (sum - a[u][v] + min(w, dp[u][v]));
}
printf("%.4lf
", res * 1.0 / q);
}
return 0;
}