题目分析
非常好的一道题,这道题同样是求树上的期望问题,也就是每个边被使用的次数乘以 每个边的权值,再除以K*K/2(因为这道题可能会抽到相同的点)因为最后还需要乘以K*K那么这样一化简很明显就是最终得到的结果乘以2就可以了,对于每条边而要,如果左选中n个顶点,右边选中m个顶点,那么很明显,这条边所做的贡献为n*m次。我们对于dp[i][j]表示以i为根节点的子树中选取j个顶点得到的最小的期望值,那么我们
利用分组背包的思想,每个子树看做一个分组,能选取的点数表示背包的容量。这样我们就可以写出 状态转移方程为dp[fa][j] = min(dp[fa][j], dp[fa][j-k]+dp[son][k] + 2*k*(K-k)*W);
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 2005;
typedef long long LL;
const LL INF = 1LL<<61;
int head[maxn],tot;
LL dp[maxn][55];
struct Edge{
int to,w,next;
}e[maxn<<1];
void addedge(int from,int to,int w){
e[tot].to = to;
e[tot].w = w;
e[tot].next = head[from];
head[from] = tot++;
}
void dfs(int u,int fa,int K){
for(int i = 2; i <= K; i++) dp[u][i] = INF;
dp[u][0] = dp[u][1] = 0;
for(int i = head[u]; i != -1; i = e[i].next){
int v = e[i].to;
int w = e[i].w;
if(v != fa){
dfs(v ,u, K);
for(int j = K; j >= 1; j--){
for(int k = 1; k <= j; k++){
dp[u][j] = min(dp[u][j], dp[u][j-k] + dp[v][k] + (LL)(K-k)*k*2*w);
}
}
}
}
}
void init(){
tot = 0;
memset(head, -1, sizeof(head));
}
int main(){
int T,n,K;
scanf("%d", &T);
while(T--){
init();
scanf("%d%d", &n, &K);
int from,to,w;
for(int i = 1; i < n; i++){
scanf("%d%d%d", &from, &to, &w);
addedge(from, to, w);
addedge(to, from, w);
}
dfs(1, -1, K);
printf("%I64d\n", dp[1][K]);
}
return 0;
}