本文共 3014 字,大约阅读时间需要 10 分钟。
给 一 棵 树 , 在 树 中 选 择 k 个 节 点 , 要 求 输 出 最 小 任 意 两 点 距 离 和 。 给一棵树,在树中选择k个节点,要求输出最小任意两点距离和。 给一棵树,在树中选择k个节点,要求输出最小任意两点距离和。
树 、 选 择 节 点 、 统 计 距 离 − 树 形 d p 。 树、选择节点、统计距离-树形dp。 树、选择节点、统计距离−树形dp。
设 f [ u ] [ i ] 为 以 u 为 根 的 字 树 中 选 择 i 个 节 点 的 贡 献 。 设f[u][i]为以u为根的字树中选择i个节点的贡献。 设f[u][i]为以u为根的字树中选择i个节点的贡献。 转 移 方 程 很 容 易 看 出 : 转移方程很容易看出: 转移方程很容易看出:f [ u ] [ i + j ] = m i n ( f [ u ] [ i + j ] , f [ u ] [ i ] + f [ v ] [ j ] + e [ i ] . w ∗ j ∗ ( k − j ) ) f[u][i+j]=min(f[u][i+j],f[u][i]+f[v][j]+e[i].w*j*(k-j)) f[u][i+j]=min(f[u][i+j],f[u][i]+f[v][j]+e[i].w∗j∗(k−j))
e [ i ] . w ∗ j ∗ ( k − j ) 是 统 计 任 意 两 点 距 离 和 的 套 路 公 式 。 e[i].w*j*(k-j)是统计任意两点距离和的套路公式。 e[i].w∗j∗(k−j)是统计任意两点距离和的套路公式。
在 方 程 中 可 以 看 出 , f [ u ] [ i + j ] 是 与 f [ u ] [ i ] 有 关 的 , 所 以 不 能 正 向 转 移 , 而 要 倒 序 转 移 。 在方程中可以看出,f[u][i+j]是与f[u][i]有关的,所以不能正向转移,而要倒序转移。 在方程中可以看出,f[u][i+j]是与f[u][i]有关的,所以不能正向转移,而要倒序转移。
但 是 倒 序 转 移 中 会 出 现 转 移 值 出 现 被 更 改 的 问 题 。 但是倒序转移中会出现转移值出现被更改的问题。 但是倒序转移中会出现转移值出现被更改的问题。
所 以 需 要 一 个 辅 助 数 组 , 这 也 是 套 路 , 但 是 不 能 m e m s e t , 可 以 手 写 。 所以需要一个辅助数组,这也是套路,但是不能memset,可以手写。 所以需要一个辅助数组,这也是套路,但是不能memset,可以手写。最 后 注 意 r o o t 不 一 定 为 1 , 需 要 记 录 度 即 可 。 最后注意root不一定为1,需要记录度即可。 最后注意root不一定为1,需要记录度即可。
#include "bits/stdc++.h"using namespace std;typedef long long ll;const int N = 2e5 + 10;int n, k;struct Edge { int v, next; ll w;}e[N << 1];int head[N << 1], cnt;ll f[N][110]; // 以u为根的子树中选择k个叶子节点ll temp[110];int siz[N], d[N];inline void add(int u, int v, int w) { e[++cnt].v = v; e[cnt].w = w; e[cnt].next = head[u]; head[u] = cnt;}void dfs(int u, int fa) { if(d[u] == 1) { siz[u] = 1, f[u][0] = f[u][1] = 0; return ; } siz[u] = 0; for(int i = 1;i <= k; i++) f[u][i] = 2e18; for(int i = head[u]; i; i = e[i].next) { int v = e[i].v; if(v == fa) continue; dfs(v, u); siz[u] += siz[v]; for(int p = 0;p <= min(siz[u], k); p++) temp[p] = 2e18; for(int p = 0;p <= min(siz[u], k); p++) { for(int q = 0;q <= siz[v] && p + q <= k; q++) { temp[p + q] = min(temp[p + q], f[u][p] + f[v][q] + e[i].w * q * (k - q)); } } for(int p = 0;p <= min(siz[u], k); p++) f[u][p] = temp[p]; }}void solve() { int _; scanf("%d",&_); int Case = 1; while(_--) { scanf("%d%d",&n,&k); cnt = 0; for(int i = 1;i <= n; i++) d[i] = 0, siz[i] = 0, head[i] = 0; for(int i = 1;i < n; i++) { int u, v; ll w; scanf("%d%d%lld",&u,&v,&w); add(u, v, w); add(v, u, w); d[u]++; d[v]++; } printf("Case #%d: ",Case++); if(n == 2) { if(k == 2) printf("%lld\n",e[1].w); else printf("0\n"); continue; } for(int i = 1;i <= n; i++) { if(d[i] > 1) { dfs(i, 0); printf("%lld\n",f[i][k]); break ; } } }}signed main() { solve();}
转载地址:http://ofjwz.baihongyu.com/