【树型dp】hdu4126

来源:互联网 发布:阿里云 收入 编辑:程序博客网 时间:2024/06/06 18:43

树型dp学习ing

一套个人感觉图论上很好的题。
题意:
有n个城镇,m条边。
现在询问q次,问v到u这条边,价值增加到w后,创造n个点的最小生成树所用的花费最少是多少。
答案累加/q

如果不增加花费的话,那明显就是一个最小生成树。
但是如果增加花费的话,有两种情况。
1:增加花费的那条边跟我一开始构成的最小生成树无关。
2:增加花费的那条边在我的最小生成树上。

第一种情况简单判断就好了。
第二种情况则需要进行分析。

由kruskal算法我们可知。最小生成树上的所有边。都是来自不使树成环的最小边。所以我们去除一条边后,最小生成树会变成两颗最小生成树,然后我们只需要再找一条连接这两颗最小生成树最短的边即可。

我们先dp出某一个点在删除与他相邻的那个点的边后,到其他任意点的最小距离。
然后我们再dp一下。找出以v和u为根的树相连接的最小距离。

当我们增加了某一条边到w时,只需要比较best[u][v]和w谁比较小即可

这道题也可以换成次小生成树。。。删边的时候假装把某条边增加到无穷大就好了

代码:

/*@resources: hdu 4126@date: 2017-08-14@author: QuanQqqqq@algorithm: tree_dp prim*/#include <bits/stdc++.h>#define ll long long#define MAXN 3005#define INF 0x3f3f3f3fusing namespace std;ll mapp[MAXN][MAXN];vector<int> vec[MAXN];int pre[MAXN]; //记录前一个点 ll dis[MAXN];bool vis[MAXN];ll dp[MAXN][MAXN];ll best[MAXN][MAXN];void init() {    for (int i = 0; i < MAXN; i++) {        for (int j = 0; j < MAXN; j++) {            mapp[i][j] = INF;            dp[i][j] = INF;        }        vec[i].clear();        dis[i] = INF;    }}void addEdge(int v, int u) {    vec[v].push_back(u);    vec[u].push_back(v);}ll prim(int n) {    for (int i = 1; i < n; i++) {        dis[i] = mapp[0][i];        vis[i] = false;        pre[i] = 0;    }    int k;    ll mst = 0;    dis[0] = INF;    vis[0] = true;    pre[0] = -1;    for (int i = 0; i < n - 1; i++) {        k = 0;        for (int j = 1; j < n; j++) {            if (!vis[j] && dis[j] < dis[k]) {                k = j;            }        }        vis[k] = true;        mst += dis[k];        if (pre[k] != -1) {            addEdge(pre[k], k);        }        for (int j = 1; j < n; j++) {            if (!vis[j] && dis[j] > mapp[j][k]) {                dis[j] = mapp[j][k];                pre[j] = k;            }        }    }    return mst;}ll dptree(int u, int fa, int root) {    int len = vec[u].size();    for (int i = 0; i < len; i++) {        int v = vec[u][i];        if (v == fa) {            continue;        }        dp[root][u] = min(dp[root][u], dptree(v, u, root));    }    if (root != fa) {        dp[root][u] = min(dp[root][u], mapp[root][u]);    }    return dp[root][u];}ll dfs(int u, int fa, int root) {    ll ans = dp[u][root];    int len = vec[u].size();    for (int i = 0; i < len; i++) {        int v = vec[u][i];        if (v == fa) {            continue;        }        ans = min(ans, dfs(v, u, root));    }    return ans;}int main() {    int n, m, u, v, q;    ll w, ans, mst;    while (~scanf("%d %d", &n, &m) && (n + m)) {        init();        for (int i = 0; i < m; i++) {            scanf("%d %d %lld", &u, &v, &w);            mapp[u][v] = mapp[v][u] = min(mapp[v][u], w);        }        mst = prim(n);        for (int i = 0; i < n; i++) {            dptree(i, -1, i);        }        for (int i = 0; i < n; i++) {            int len = vec[i].size();            for (int j = 0; j < len; j++) {                v = vec[i][j];                best[i][v] = best[v][i] = dfs(v, i, i);            }        }        scanf("%d", &q);        ans = 0;        for (int i = 0; i < q; i++) {            scanf("%d %d %lld", &v, &u, &w);            if (pre[v] != u && pre[u] != v) {                ans += mst;            } else {                ans += mst - mapp[u][v] + min(best[u][v], w);            }        }        printf("%.4lf\n", ans * 1.0 / q);    }}/*3 30 1 30 2 21 2 530 2 31 2 60 1 6*/