[学习笔记]DP模型·路径长度之和

本文介绍了一种使用树形动态规划(DP)解决无根带权树中选择K个节点,使路径长度之和最小的问题。通过建立f、g、h三个状态的DP模型,详细阐述了状态转移方程,并分析了复杂度,最终给出了问题的解决方案。

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

一、问题

给定一棵n个节点的无根带权树,要从中选出K个不同的点A1,A2,...,AK,最小化K1i=1dist(Ai,Ai+1)的值。n3000,kndist(u,v)为树上uv的距离。

二、模型建立

可以把问题看作选出K个不同的点A1,A2,...,AK,然后从A1走到A2,再走到A3,…,最后走到AK的最小代价。
把原图视为有根树,然后树形DP。由于要选出K个点,所以考虑树形背包DP,定义f[u][i]表示在u的子树内选出i个点的最小代价,但是这样是没有正确性的。因为将两个子树合并时,有可能不断地在这两个子树内来回走。所以DP模型改为:
f[u][i]u的子树内选出i个点,第1个点和第i个点都为u的最小代价,也就是在u的子树内,从u开始,最后回到u
g[u][i]u的子树内选出i个点,第1个点为u的最小代价,也就是在u的子树内,从u开始,最后回到u的子树内的任一节点。
h[u][i]u的子树内选出i个点,这i个点中存在一个点为u的最小代价,也就是在u的子树内,从任意节点开始,最后回到子树内的任意节点,但必须经过点u
容易得出最后结果为minni=1h[i][K]

三、转移

一开始时,f[u][1]=g[u][1]=h[u][1]=0
以下设当前枚举到了u的子节点vf[u][i]表示u的已经枚举过的子树的f[u][i]gh同理。以下的图中,红色区域为u的已经枚举过的子树,黄色区域为v的子树。val(u,v)表示边(u,v)的权值。

f的转移

如下图,即从u开始,在红色区域走一圈后回到u,再到v,在黄色区域走一圈后又回到v,最后回到u,在这个路程中,边(u,v)被走了两次,所以转移为:
f[u][i+j]=min(f[u][i+j],f[u][i]+f[v][j]+val(u,v)2)
这里写图片描述

g的转移

转移一:终点在黄色区域。如下图,从u开始,在红色区域走一圈后回到u,再到v,最后回到黄色区域的任意节点。在这个路程中,边(u,v)被走了一次,所以转移为:
g[u][i+j]=min(g[u][i+j],f[u][i]+g[v][j]+val(u,v))
这里写图片描述
转移二:终点在红色区域。如下图,从u开始,先到v,在黄色区域里走一圈后回到v,再回到u,最后回到红色区域的任意节点。在这个路程中,边(u,v)被走了两次,所以转移为:
g[u][i+j]=min(g[u][i+j],g[u][i]+f[v][j]+val(u,v)2)
这里写图片描述

h的转移

转移一:起点和终点一个在红色区域,一个在黄色区域。此时一定是从其中一个区域出发,到达u之后回到另一个区域内。在这个路程中,边(u,v)被走了一次,所以转移为:
h[u][i+j]=min(h[u][i+j],g[u][i]+g[v][j]+val(u,v))
这里写图片描述
转移二:起点和终点都在红色区域。也就是从红色区域的任意一点出发,到达u后走到v,在黄色区域走一圈后回到v,再走到u,最后回到红色区域内的任意节点。在这个路程中,边(u,v)被走了两次,所以转移为:
h[u][i+j]=min(h[u][i+j],h[u][i]+f[v][j]+val(u,v)2)
这里写图片描述
转移三:起点和终点都在黄色区域。也就是从黄色区域的任意一点出发,到达v后走到u,在红色区域走一圈后回到u,再走到v,最后回到黄色区域内的任意节点。在这个路程中,边(u,v)被走了两次,所以转移为:
h[u][i+j]=min(h[u][i+j],f[u][i]+h[v][j]+val(u,v)2)
这里写图片描述

实现细节

考虑到有可能黄色区域没有被经过。
所以要首先令f[u][i]=f[u][i],g[u][i]=g[u][i],h[u][i]=h[u][i]

四、复杂度分析

看上去是O(n3)的,但是第二维i的上界只有u的子树大小,因此两层分别枚举两个区域的大小,相当于每一对点都只在lca处被计算了一次,所以复杂度为O(n2)

五、代码

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || /*orz mx & xmk*/ c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 3005, M = 6005, INF = 0x3f3f3f3f;
int n, K, ecnt, nxt[M], adj[N], go[M], f[N][N], g[N][N], h[N][N], sze[N],
ans = 0x3f3f3f3f, val[M], x[N], y[N], z[N];
void chkmin(int &a, int b) {if (b < a) a = b;}
void add_edge(int u, int v, int w) {
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; val[ecnt] = w;
    nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u; val[ecnt] = w;
}
void dfs(int u, int fu) {
    int i, j; sze[u] = 1; memset(f[u], INF, sizeof(f[u]));
    memset(g[u], INF, sizeof(g[u])); memset(h[u], INF, sizeof(h[u]));
    f[u][1] = g[u][1] = h[u][1] = 0;
    for (int e = adj[u], v; e; e = nxt[e]) {
        if ((v = go[e]) == fu) continue; dfs(v, u);
        for (i = 1; i <= sze[u] + sze[v]; i++) x[i] = f[u][i],
            y[i] = g[u][i], z[i] = h[u][i];
        for (i = 1; i <= sze[u]; i++) for (j = 1; j <= sze[v]; j++) {
            chkmin(x[i + j], f[u][i] + (val[e] << 1) + f[v][j]);
            chkmin(y[i + j], f[u][i] + val[e] + g[v][j]);
            chkmin(y[i + j], (val[e] << 1) + f[v][j] + g[u][i]);
            chkmin(z[i + j], g[u][i] + val[e] + g[v][j]);
            chkmin(z[i + j], h[u][i] + (val[e] << 1) + f[v][j]);
            chkmin(z[i + j], h[v][j] + (val[e] << 1) + f[u][i]);
        }
        sze[u] += sze[v]; for (i = 1; i <= sze[u]; i++)
            f[u][i] = x[i], g[u][i] = y[i], h[u][i] = z[i];
    }
    if (K <= sze[u]) chkmin(ans, h[u][K]);
}
int main() {
    int i, x, y, z; n = read(); K = read();
    for (i = 1; i < n; i++) x = read(), y = read(), z = read(),
        add_edge(x, y, z); dfs(1, 0);
    cout << ans << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值