标签:
http://acm.hdu.edu.cn/showproblem.php?pid=5148
1 2 2 1 2 1
2
/**
hdu 5148 树形dp+分组背包
题目大意:选出K个点v1,v2,...vK使得∑Ki=1∑Kj=1dis(vi,vj)最小.
解题思路:
考虑每条边的贡献,一条边会把树分成两部分,若在其中一部分里选择了x个点,则这条边被统计的次数为x*(K-x)*2.
那么考虑dp[u][i]表示在u的子树中选择了i个点的最小代价,有转移dp[u][i]=minKj=0(dp[u][i?j]+dp[v][j]+j*(K?j)*2*wu,v),式子中u为v的父亲,wu,v表示(u,v)这条边的长度.
把选取的点看做背包容量,每个子树看做一类物品,从每一类中最多选取一件物品,问背包可获得的最少价值。
时间复杂度O(nK^2).
*/
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;
const int N=2055;
const long long inf = 200000 * 10000000LL;
int n,k;
struct note
{
int v,w,next;
}edge[N*2];
int head[N],ip;
void init()
{
memset(head,-1,sizeof(head));
ip=0;
}
void addedge(int u,int v,int w)
{
edge[ip].v=v,edge[ip].w=w,edge[ip].next=head[u],head[u]=ip++;
}
long long dp[N][55];
void dfs(int u,int pre)
{
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(v==pre) continue;
dfs(v,u);
for(int j=k;j>=0;j--)
{
for(int l=1;l<=j;l++)
{
dp[u][j]=min(dp[u][j],dp[u][j-l]+dp[v][l]+(k-l)*l*2*edge[i].w);
}
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
init();
scanf("%d%d",&n,&k);
for(int i=0;i<n-1;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
///由于去最小,初始化除了dp[i][0]和dp[i][1]都要设为无穷大
memset(dp,0,sizeof(dp));
for (int i = 1; i <= n; i++)
for (int j = 2; j <= k; j++)
dp[i][j] = inf;
dfs(1,-1);
printf("%I64d\n",dp[1][k]);
}
return 0;
}标签:
原文地址:http://blog.csdn.net/lvshubao1314/article/details/42058795