![](https://img-blog.csdn.net/20171223210110343?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYnpqcl9Mb2dfeA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
分析 树形DP
经典的模型依旧只会暴力,果然蒟蒻呀- 设
f[u][j]
表示从点
u
出发,在u的子树中经过
j
个点最后回到点u的最小距离和
- 设
g[u][j]
表示从点
u
出发,在u的子树中经过
j
个点最后停在任意一点(其实也相当于从u的子树中任意一点出发,经过
j
个点最后回到点u)的最小距离和
- 设
h[u][j]
表示在
u
的子树中从任意一点出发,经过j个点并保证经过点
u
,最后停在任意一点的最小距离和
- 显然f,g,h都是由
u
的子节点v转移过来,则我们可以得到如下转移(为了描述方便,设已经处理完的
u
的子节点的子树集合为A,当前处理的子节点
v
的子树为B,用
→
表示每种转移所对应的在树上走的方案)
- 对于
f[u][j]
:
-
f′[u][j+k]=min(f[v][k]+f[u][j]+2⋅dis(u,v))(u→A→u→B→u)
- 对于
g[u][j]
:
-
g′[u][j+k]=min(g[v][k]+f[u][j]+dis(u,v))(u→A→u→B)
-
g′[u][j+k]=min(f[v][k]+g[u][j]+2⋅dis(u,v))(u→B→u→A)
- 对于
h[u][j]
:
-
h′[u][j+k]=min(f[v][k]+h[u][j]+2⋅dis(u,v))(A→u→B→u→A)
-
h′[u][j+k]=min(h[v][k]+f[u][j]+2⋅dis(u,v))(B→u→A→u→B)
-
h′[u][j+k]=min(g[v][k]+g[u][j]+dis(u,v))(A→u→B)
- 时间复杂度的证明也是比较经典了,每次枚举的是
szeu⋅szev
,相当于每次从
A,B
中各任选一点,它们的
LCA
为
u
,这样的点对枚举不会重复,因此总的时间复杂度为O(n2)
代码
#include <iostream>
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
using namespace std;
namespace INOUT
{
const int S = 1 << 20;
char frd[S], *hed = frd + S;
const char *tal = hed;
inline char nxtChar()
{
if (hed == tal)
fread(frd, 1, S, stdin), hed = frd;
return *hed++;
}
inline int get()
{
char ch; int res = 0; bool flag = false;
while (!isdigit(ch = nxtChar()) && ch != '-');
(ch == '-' ? flag = true : res = ch ^ 48);
while (isdigit(ch = nxtChar()))
res = res * 10 + ch - 48;
return flag ? -res : res;
}
};
using namespace INOUT;
const int Maxn = 0x3f3f3f3f;
const int N = 3005;
int n, Ans = Maxn, K, sze[N];
int f[N][N], g[N][N], h[N][N];
int ff[N][N], gg[N][N], hh[N][N];
struct Edge
{
int to, cst; Edge *nxt;
}p[N << 1], *T = p, *lst[N];
inline void LinkEdge(int x, int y, int z)
{
(++T)->nxt = lst[x]; lst[x] = T; T->to = y; T->cst = z;
(++T)->nxt = lst[y]; lst[y] = T; T->to = x; T->cst = z;
}
inline int Min(int x, int y) {return x < y ? x : y;}
inline void CkMin(int &x, int y) {if (x > y) x = y;}
inline void Dfs(int u, int fa)
{
sze[u] = 1;
h[u][1] = g[u][1] = f[u][1] = 0;
for (Edge *e = lst[u]; e; e = e->nxt)
{
int v = e->to;
if (v == fa) continue;
Dfs(v, u);
for (int j = 1, jm = sze[u] + sze[v]; j <= jm; ++j)
{
ff[u][j] = f[u][j];
gg[u][j] = g[u][j];
hh[u][j] = h[u][j];
}
int L1 = e->cst, L2 = e->cst << 1;
for (int j = 1; j <= sze[u]; ++j)
for (int k = 1; k <= sze[v]; ++k)
{
CkMin(ff[u][j + k], f[v][k] + f[u][j] + L2);
CkMin(gg[u][j + k], g[v][k] + f[u][j] + L1);
CkMin(gg[u][j + k], f[v][k] + g[u][j] + L2);
CkMin(hh[u][j + k], f[v][k] + h[u][j] + L2);
CkMin(hh[u][j + k], h[v][k] + f[u][j] + L2);
CkMin(hh[u][j + k], g[v][k] + g[u][j] + L1);
}
for (int j = 1, jm = sze[u] + sze[v]; j <= jm; ++j)
{
f[u][j] = ff[u][j];
g[u][j] = gg[u][j];
h[u][j] = hh[u][j];
}
sze[u] += sze[v];
}
CkMin(Ans, h[u][K]);
}
int main()
{
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
n = get(); K = get(); int x, y;
for (int i = 1; i < n; ++i)
{
x = get(); y = get();
LinkEdge(x, y, get());
}
memset(f, Maxn, sizeof(f));
memset(g, Maxn, sizeof(g));
memset(h, Maxn, sizeof(h));
Dfs(1, 0);
printf("%d\n", Ans);
fclose(stdin); fclose(stdout);
return 0;
}