hdu 4126 Genghis Khan the Conqueror

本文探讨了树形动态规划与Prim算法的结合应用,详细解释了如何使用Prim算法构建最小生成树,并在更新边的情况下计算新的最小生成树。通过引入动态规划思想,文章提出了一种高效的方法来计算在不同情况下生成树之间的最短距离,适用于图论和算法领域的研究与实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

         prim + 树形dp,首先用prim预处理,建出最小生成树,然后,如果更新的不是最小生成树上的边,那么新图中的最小生成树即为原来的最小生成树。如果更新的为最小生成树上的边,则我们需要考虑两个方面:

       1、将原来最小生成树的边,换成新边的新生成树的长度。

       2、把原来的边删除,原图上从新生成最小生成树。

第一个问题比较容易解决,直接用原生成树,算就可以。仔细想一下第二个问题,我们可以发现,如果在原来最小生成树的基础上把一条边删除然后替换成其他边的话,相当于求两个最小生成树的次小距离。这样的话,我们只需要像prim求最小生成树的思想一样,利用一颗树中的点,去更新到另一颗树的距离,就可求出最短的距离了(删掉原来的那条边),这样的话时间复杂度是O(n*n),可以接受。dp[ u ][ v ]表示把边u - v 边删去,两颗树的最短距离。


#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#define LL long long
#define CLR(a, b) memset(a, b, sizeof(a))

using namespace std;

const int N = 3003;
const int INF = 0x3f3f3f3f;

struct Edge
{
    int u, v, c;
}G[N * N * 2], E[N << 1];

int G_fir[N], G_next[N * N * 2];
int n, G_tot, d[N], fa[N];
int fir[N], next[N << 1], tot;
int mark[N][N];
int map[N][N];
bool vis[N];
int dp[N][N], pri;

void G_Add_Edge(int u, int v, int c)
{
    map[u][v] = c;
    G[G_tot].u = u, G[G_tot].v = v, G[G_tot].c = c;
    G_next[G_tot] = G_fir[u], G_fir[u] = G_tot ++;
}

void Add_Edge(int u, int v)
{
    E[tot].u = u, E[tot].v = v;
    next[tot] = fir[u], fir[u] = tot ++;
}

void prim()
{
    int i, num = 1, u = 0, best = INF, v, f, j;
    CLR(vis, 0);CLR(mark, -1);pri = 0;
    for(i = 0; i < n; i ++) d[i] = INF;
    while(num < n)
    {
        vis[u] = 1;best = INF;
        for(i = G_fir[u]; ~i; i = G_next[i])
        {
            v = G[i].v;
            if(!vis[v] && d[v] >= G[i].c)
            {
                fa[v] = i;
                d[v] = G[i].c;
            }
        }
        for(i = 0; i < n; i ++)
        {
            if(!vis[i] && d[i] < best)
            {
                best = d[i];
                j = fa[i];
            }
        }
        pri += best;
        mark[G[j].v][G[j].u] = mark[G[j].u][G[j].v] = best;
        Add_Edge(G[j].u, G[j].v);
        Add_Edge(G[j].v, G[j].u);
        u = G[j].v;
        num ++;
    }
}

int dfs(int pos, int u, int fa)//用点pos去更新任意两颗生成树的距离
{
    int i, v, j, ret = INF, cmp;
    for(i = fir[u]; ~i; i = next[i])
    {
        v = E[i].v;
        if(v != fa)
        {
            cmp = dfs(pos, v, u);
            ret = min(ret, cmp);
            dp[u][v] = dp[v][u] = min(dp[u][v], cmp);
        }
    }
    if(fa != pos)
        ret = min(ret, map[pos][u]);
    return ret;
}

int main()
{
    //freopen("input.txt", "r", stdin);
    int m, i, j , u, v, c, q;
    double ans;
    while(scanf("%d%d", &n, &m), n + m)
    {
        G_tot = tot = 0; CLR(fir, -1);
        CLR(G_fir, -1);CLR(dp, INF);
        CLR(map, INF);
        for(i = 0; i < m; i ++)
        {
            scanf("%d%d%d", &u, &v, &c);
            G_Add_Edge(u, v, c);
            G_Add_Edge(v, u, c);
        }
        prim();
        for(i = 0; i < n; i ++)
        {
            dfs(i, i, -1);
        }
        scanf("%d", &q);ans = 0;
        for(i = 0; i < q; i ++)
        {
            scanf("%d%d%d", &u, &v, &c);
            if(mark[u][v] != -1)
            {
                ans += min(c, dp[u][v]) + pri - mark[u][v];
            }
            else
            {
                ans += pri;
            }
        }
        printf("%.4lf\n", ans / q);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值