【题目链接】
【思路要点】
- 考虑\(N=K\)的情况,我们要求的即为所有边权之和的两倍减去直径。
- 因此我们本质上需要做的是求出原树的一个大小为\(K\)的联通块,使得联通块内部的边权之和的两倍减去直径最小。
- 经典树上背包,时间复杂度\(O(N^2)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 3005; const int INF = 1e9; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct edge {int dest, len; }; vector <edge> a[MAXN]; int n, k, size[MAXN], dp[MAXN][MAXN][3]; void dpmerge(int pos, int tmp, int len) { for (int i = size[pos] + 1; i <= size[pos] + size[tmp]; i++) dp[pos][i][0] = dp[pos][i][1] = dp[pos][i][2] = INF; for (int i = size[pos]; i >= 1; i--) for (int j = 1; j <= size[tmp]; j++) { chkmin(dp[pos][i + j][0], dp[pos][i][0] + dp[tmp][j][0] + len * 2); chkmin(dp[pos][i + j][1], dp[pos][i][1] + dp[tmp][j][0] + len * 2); chkmin(dp[pos][i + j][1], dp[pos][i][0] + dp[tmp][j][1] + len); chkmin(dp[pos][i + j][2], dp[pos][i][2] + dp[tmp][j][0] + len * 2); chkmin(dp[pos][i + j][2], dp[pos][i][1] + dp[tmp][j][1] + len); chkmin(dp[pos][i + j][2], dp[pos][i][0] + dp[tmp][j][2] + len * 2); } size[pos] += size[tmp]; } void work(int pos, int fa) { size[pos] = 1; memset(dp[pos][1], 0, sizeof(dp[pos][1])); for (unsigned i = 0; i < a[pos].size(); i++) { int tmp = a[pos][i].dest; if (tmp == fa) continue; work(tmp, pos); dpmerge(pos, tmp, a[pos][i].len); } } int main() { read(n), read(k); for (int i = 1; i <= n - 1; i++) { int x, y, z; read(x), read(y), read(z); a[x].push_back((edge) {y, z}); a[y].push_back((edge) {x, z}); } work(1, 0); int ans = INF; for (int i = 1; i <= n; i++) if (size[i] >= k) chkmin(ans, dp[i][k][2]); writeln(ans); return 0; }