一、问题
给定一棵n个节点的无根带权树,要从中选出
二、模型建立
可以把问题看作选出K个不同的点
把原图视为有根树,然后树形DP。由于要选出K个点,所以考虑树形背包DP,定义
f[u][i]:u的子树内选出
g[u][i]:u的子树内选出
容易得出最后结果为minni=1h[i][K]。
三、转移
一开始时,f[u][1]=g[u][1]=h[u][1]=0。
以下设当前枚举到了u的子节点
f的转移
如下图,即从u开始,在红色区域走一圈后回到
f[u][i+j]=min(f[u][i+j],f′[u][i]+f[v][j]+val(u,v)∗2)
g的转移
转移一:终点在黄色区域。如下图,从u开始,在红色区域走一圈后回到
g[u][i+j]=min(g[u][i+j],f′[u][i]+g[v][j]+val(u,v))
转移二:终点在红色区域。如下图,从u开始,先到
g[u][i+j]=min(g[u][i+j],g′[u][i]+f[v][j]+val(u,v)∗2)
h的转移
转移一:起点和终点一个在红色区域,一个在黄色区域。此时一定是从其中一个区域出发,到达u之后回到另一个区域内。在这个路程中,边
h[u][i+j]=min(h[u][i+j],g′[u][i]+g[v][j]+val(u,v))
转移二:起点和终点都在红色区域。也就是从红色区域的任意一点出发,到达u后走到
h[u][i+j]=min(h[u][i+j],h′[u][i]+f[v][j]+val(u,v)∗2)
转移三:起点和终点都在黄色区域。也就是从黄色区域的任意一点出发,到达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的上界只有
五、代码
#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;
}