noi.ac 9

题目链接

解法

首先考虑当n==k时,答案就是所有边权2-直径的长度。然后k<n时,就是求一个大小为k的联通块,使得联通块内的所有边权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;
} 

发布了95 篇原创文章 · 获赞 9 · 访问量 3186

猜你喜欢

转载自blog.csdn.net/wmhtxdy/article/details/104390020
今日推荐