HDU4126 - 最小生成树 + DP

HDU4126 - 最小生成树 + DP

题目链接

题目大意

给你一个图,有q次询问,每次询问如果替换一条边后最小生成树的大小是多少,求q次询问的平均值。(要替换的边的权一定不小于原来边权,替换只在当前次询问有效)

数据范围

多组输入T<=20,1N3000,0MNN,边权ci1071Q10000

解题思路

首先找到一颗最小生成树,之后当要替换的边不在最小生成树里的话,当前的最小生成树就直接是最小生成树的值。如果要替换的边在最小生成树里面的话,比如(u,v)边是要替换的边,将(u,v)删掉之后就会形成两个子树,就需要找出一条能连接这两个子树最小的那条边,那我们就可以实现处理出最小生成树上每条边的最小替换值。假如要替换的边一端是点p,那么假如p在u子树中,只需要在v子树中找到一个连接p的一条最小的边。那么做n次dfs,每次dfs处理以i点为根,且以i作为要替换的一个端点时(u,v)边的最小替换值,这样就n2处理出了最小生成树中所有边的最小替换值。而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\n", res * 1.0 / q);
    }
    return 0;
}
posted @ 2018-07-20 09:40  呵呵!!!  阅读(112)  评论(0编辑  收藏  举报