noi.ac 9

题目链接

解法

首先考虑当n==k时,答案就是 所 有 边 权 ∗ 2 − 直 径 的 长 度 所有边权*2-直径的长度 2。然后k<n时,就是求一个大小为k的联通块,使得联通块内的 所 有 边 权 ∗ 2 − 直 径 的 长 度 所有边权*2-直径的长度 2最小。考虑树型背包。
设f[i][j][0]表示以i为根的子树,选了j个点(i自己也在这个联通块里),总边权*2的最小值.f[i][j][1]表示以i为根的子树,从i开始一口气选了j个点组成的一条链的答案最小是多少。f[i][j][2]表示以i为根的子树,选了一个大小为j的联通块(包含i),答案是多少。
转移时稍微画一下图就可以写出式子了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e3+5;
inline int read(){
	char c=getchar();int t=0,f=1;
	while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
	while((isdigit(c))&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n,k;
struct edge{
	int v,p,w;
}e[maxn<<1];
int h[maxn],cnt;
inline void add(int a,int b,int c){
	e[++cnt].p=h[a];
	e[cnt].v=b;
	e[cnt].w=c;
	h[a]=cnt;
	e[++cnt].p=h[b];
	e[cnt].v=a;
	e[cnt].w=c;
	h[b]=cnt;
}
int val[maxn],f[maxn][maxn][3];
void dfs(int u,int fa){
	for(int i=h[u];i;i=e[i].p){
		int v=e[i].v;
		if(v==fa)continue;
		val[v]=e[i].w;
		dfs(v,u);
	}
}
int sz[maxn];
const int inf=0x3f3f3f3f;
void dp(int u,int fa){
	sz[u]=1;
	for(int i=h[u];i;i=e[i].p){
		int v=e[i].v;
		if(v==fa)continue;
		dp(v,u);
		for(int i=sz[u]+1;i<=sz[u]+sz[v];i++)f[u][i][0]=f[u][i][1]=f[u][i][2]=inf;
		for(int i=sz[u];i>=1;i--){
			for(int j=1;j<=sz[v];j++){
				f[u][i+j][0]=min(f[u][i+j][0],f[u][i][0]+f[v][j][0]+val[v]*2);
				f[u][i+j][1]=min(f[u][i+j][1],f[u][i][1]+f[v][j][0]+val[v]*2);
				f[u][i+j][1]=min(f[u][i+j][1],f[u][i][0]+f[v][j][1]+val[v]);
				f[u][i+j][2]=min(f[u][i+j][2],f[u][i][2]+f[v][j][0]+val[v]*2);
				f[u][i+j][2]=min(f[u][i+j][2],f[u][i][1]+f[v][j][1]+val[v]);
				f[u][i+j][2]=min(f[u][i+j][2],f[u][i][0]+f[v][j][2]+val[v]*2);
			}
		}
		sz[u]+=sz[v];
	}
}
int main(){
	n=read(),k=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read(),w=read();
		add(u,v,w);
	}
	dfs(1,0);
	dp(1,0);
	int ans=inf;
	for(int i=1;i<=n;i++){
		if(sz[i]>=k)ans=min(ans,f[i][k][2]);
	}
	printf("%d\n",ans);
	return 0;
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值