4987: Tree
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 11 Solved: 8
[ Submit][ Status][ Discuss]
Description
从前有棵树。
找出K个点A1,A2,…,Ak。
使得∑dis(AiAi+1),(1<=i<=K-1)最小。
Input
第一行两个正整数n,k,表示数的顶点数和需要选出的点个数。
接下来n-l行每行3个非负整数x,y,z,表示从存在一条从x到y权值为z的边。
I<=k<=n。
l<x,y<=n
1<=z<=10^5
n <= 3000
Output
一行一个整数,表示最小的距离和。
Sample Input
10 7
1 2 35129
2 3 42976
3 4 24497
2 5 83165
1 6 4748
5 7 38311
4 8 70052
3 9 3561
8 10 80238 —————- ——————— 树形背包dp 对于选出的相邻的点,如果从 ak 再走回 a1 ,那么就相当于每条边经过了两次,但由于题目没有包含 dis(ak,a1) ,因此就相当于选出的点中的一条链可以只经过一次,其余的需要经过两次。 那我们就可以将选点转化为选边,然后考虑树形背包: 设 f[i][j][k] 表示以 i 为根的子树中选择点i ,共选出
j
条边,且包含的链端点数目为k 的最小代价。 当
k=0
时,相当于要从根节点遍历一遍选出的边然后再从根节点出去;
k=1
时,相当于从根节点遍历一遍,到达某链端点后不出去;
k=2
时相当于从某端点遍历到根节点,然后出去再回来到另一点。 对于根节点与子节点之间的边,显然当
k=0
或
k=2
时计算两遍,否则计算一遍。 这里第二维和第三维都满足背包性质,然后就可以树形背包了。 时间复杂度
O(4.5n2)
1 2 35129
2 3 42976
3 4 24497
2 5 83165
1 6 4748
5 7 38311
4 8 70052
3 9 3561
8 10 80238 —————- ——————— 树形背包dp 对于选出的相邻的点,如果从 ak 再走回 a1 ,那么就相当于每条边经过了两次,但由于题目没有包含 dis(ak,a1) ,因此就相当于选出的点中的一条链可以只经过一次,其余的需要经过两次。 那我们就可以将选点转化为选边,然后考虑树形背包: 设 f[i][j][k] 表示以 i 为根的子树中选择点
#include<set>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=3005;
template<class T>void read(T &res){
static char ch;T flag=1;
while((ch=getchar())<'0'||ch>'9')if(ch=='-')flag=-1;res=ch-48;
while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-48;res*=flag;
}
struct data{
int nxt,to,w;
}E[N<<1];
int head[N],sz[N],dp[N][N][3],n,m,ans=0x3f3f3f3f,tot;
void addedge(int x,int y,int z){
E[++tot].nxt=head[x],head[x]=tot,E[tot].to=y,E[tot].w=z;
}
void dfs(int u,int f){
sz[u]=1;dp[u][0][0]=dp[u][0][1]=0;
for(register int v,i=head[u];i;i=E[i].nxt)
if(v=E[i].to,v!=f){
dfs(v,u);
for(register int j=sz[u]-1;~j;j--)
for(register int k=sz[v]-1;~k;k--)
for(register int l=2;~l;l--)
for(register int r=l;~r;r--)
dp[u][j+k+1][l]=min(dp[u][k+j+1][l],dp[u][j][l-r]+dp[v][k][r]+E[i].w*(2-(r&1)));
sz[u]+=sz[v];
}
}
int main(){
read(n),read(m);
for(register int x,y,z,i=1;i<n;i++)
read(x),read(y),read(z),addedge(x,y,z),addedge(y,x,z);
memset(dp,0x3f,sizeof(dp));
dfs(1,0);
for(register int i=1;i<=n;i++)
for(register int j=0;j<=2;++j)ans=min(ans,dp[i][m-1][j]);
printf("%d\n",ans);
return 0;
}