タイトル効果:N個のノードの所与の木、任意の最小距離のツリーの2つのリーフノードは、最小であるように、M個のリーフノードが選択されている右側つつ。
ソリューション:木の観点からの寄与とサイドの問題で任意の2点が考慮されるべきです。
セット\(F [U] [I] \)のuをルートとするサブツリーを示し、最適解は、リーフノードから選択されたI、状態遷移方程式は:
\ [F [U] [iが=] Jを+分(F [U] [iが
+ J]、[U] [i]を+ F [v] [J] + W *のJ×(J F - M))\] 前記子ノードに加えて、親ノード側との間に貢献。
コードは以下の通りです
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
int T, kase = 0;
scanf("%d", &T);
while (T--) {
int n, m;
scanf("%d %d", &n, &m);
vector<vector<pair<int, LL>>> adj(n + 1); // <to, w>
vector<int> deg(n + 1);
for (int i = 1; i < n; i++) {
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
adj[x].emplace_back(y, z);
adj[y].emplace_back(x, z);
++deg[x], ++deg[y];
}
if (m == 1) {
printf("Case #%d: 0\n", ++kase);
continue;
}
if (n == 2) {
printf("Case #%d: %lld\n", ++kase, adj[1][0].second);
continue;
}
vector<vector<LL>> f(n + 1, vector<LL>(m + 1, 1e12));
function<int(int, int)> dfs = [&](int u, int fa) -> int {
bool not_leaf = 0;
int sz = 0;
f[u][0] = 0;
for (auto t : adj[u]) {
int v = t.first;
LL w = t.second;
if (v == fa) {
continue;
}
not_leaf = 1;
int son = dfs(v, u);
for (int i = min(sz, m); i >= 0; i--) {
for (int j = min(son, m - i); j >= 0; j--) {
if (i + j > m) {
continue;
}
f[u][i + j] = min(f[u][i + j], f[u][i] + f[v][j] + (LL)j * (m - j) * w);
}
}
sz += son;
}
if (not_leaf == 0) {
f[u][1] = 0;
sz = 1;
}
return sz;
};
int rt = 0;
for (int i = 1; i <= n; i++) {
if (deg[i] > 1) {
rt = i;
break;
}
}
dfs(rt, 0);
printf("Case #%d: %lld\n", ++kase, f[rt][m]);
}
return 0;
}