2021.10.30 模拟赛

该博客介绍了如何利用树形动态规划(DP)解决选取k个点形成连通子树,使得路径代价最小的问题。博主详细阐述了算法思想,即路径从树的直径开始,遇到分叉时选择一条分支,并返回直径,同时分析了直径上的边权只计算一次,其他边权计算两次。博主给出了O(n^2)时间复杂度的代码实现,涉及了树的遍历和状态转移方程。
摘要由CSDN通过智能技术生成

T1 set

  选取的 k 个点在树上形成一个连通子树,然后我们考虑树形 DP。我们知道所选的路径一定是从树的直径上往下走,然后遇到分叉就走过去,然后再回到直径上。所以直径上的边权只算一次,其他的边权算两次。

  设 f i , j , k f_{i, j, k} fi,j,k 表示起点和终点有 k 个在以 i 为根的子树中,且选了 j 个点时的最小代价。那么我们有:

f x , j + k , 2 = min ⁡ k = 0 , j = 0 k ≤ s i z e [ y ] , j ≤ s i z e [ x ] { f x , j , 2 + f y , k . , 0 + 2 w i ,    f x , j , 1 + f y , k , 1 + w i ,    f x , j , 0 + f y , k , 2 + 2 w i } f x , j + k , 1 = min ⁡ k = 0 , j = 0 k ≤ s i z e [ y ] , j ≤ s i z e [ x ] { f x , j , 0 + f y , k , 1 + w i ,    f x , j , 1 + f y , k , 0 + 2 w i } f x , j + k , 0 = min ⁡ k = 0 , j = 0 k ≤ s i z e [ y ] , j ≤ s i z e [ x ] { f x , j , 0 + f y , k , 0 + 2 w i } \begin{aligned} & f_{x, j+k, 2} = \min_{k=0, j = 0}^{k \leq size[y], j \leq size[x]}\lbrace f_{x, j, 2} + f_{y, k., 0} + 2w_i , \; f_{x, j, 1} + f_{y, k, 1} + w_i, \; f_{x, j, 0} + f_{y, k, 2} + 2w_i \rbrace \\ \\ & f_{x, j +k, 1} = \min_{k=0, j=0}^{k \leq size[y],j \leq size[x]} \lbrace f_{x, j, 0} + f_{y, k, 1} + w_i, \; f_{x, j, 1} + f_{y, k, 0} + 2w_i \rbrace \\ \\ & f_{x, j + k, 0} = \min_{k=0,j=0}^{k \leq size[y],j \leq size[x]} \lbrace f_{x, j, 0} + f_{y, k, 0} + 2w_i \rbrace \end{aligned} fx,j+k,2=k=0,j=0minksize[y],jsize[x]{fx,j,2+fy,k.,0+2wi,fx,j,1+fy,k,1+wi,fx,j,0+fy,k,2+2wi}fx,j+k,1=k=0,j=0minksize[y],jsize[x]{fx,j,0+fy,k,1+wi,fx,j,1+fy,k,0+2wi}fx,j+k,0=k=0,j=0minksize[y],jsize[x]{fx,j,0+fy,k,0+2wi}

  其中 size 表示这颗以 x 为根的子树的大小,时间复杂度均摊下来是 O ( n 2 ) O(n^2) O(n2).代码如下:

#include<bits/stdc++.h>
using namespace std;
#define MAXN 5050
#define MAXM 10010
#define INFI 0x3f3f3f3f

int ans = 0; 
int n = 0; int k = 0;

int tot = 0;
int first[MAXN] = { 0 };
int   nxt[MAXM] = { 0 };
int    to[MAXM] = { 0 };
int value[MAXM] = { 0 };

void add(int x, int y, int weight){
	nxt[++tot] = first[x];
	first[x] = tot;
	to[tot] = y;
	value[tot] = weight;
}

int size[MAXN] = { 0 };
int f[MAXN][MAXN][5] = { 0 };
void dfs(int x, int fa){
	size[x] = 1;
	f[x][1][0] = 0; f[x][1][1] = 0; f[x][1][2] = 0;
	for(int e = first[x]; e; e = nxt[e]){
		int y = to[e];
		if(y == fa) continue;
		dfs(y, x);
		for(int j = size[x]; j; j--){
			for(int k = size[y]; k; k--){
				f[x][j+k][2] = min(f[x][j+k][2], f[x][j][0] + f[y][k][2] + 2 * value[e]);
				f[x][j+k][2] = min(f[x][j+k][2], f[x][j][1] + f[y][k][1] + value[e]);
				f[x][j+k][2] = min(f[x][j+k][2], f[x][j][2] + f[y][k][0] + 2 * value[e]);
				
				f[x][j+k][1] = min(f[x][j+k][1], f[x][j][0] + f[y][k][1] + value[e]);
				f[x][j+k][1] = min(f[x][j+k][1], f[x][j][1] + f[y][k][0] + 2 * value[e]);
				
				f[x][j+k][0] = min(f[x][j+k][0], f[x][j][0] + f[y][k][0] + 2 * value[e]);
			}
		}
		size[x] += size[y];
	}
	ans = min(ans, f[x][k][2]);
}

int main(){
	scanf("%d%d", &n, &k);
	for(int i = 1; i < n; i++){
		int x, y, w;
		scanf("%d%d%d", &x, &y, &w);
		add(x, y, w); add(y, x, w);
	}
	
	ans = INFI;
	memset(f, 0x3f, sizeof(f));
	
	dfs(1, 0);
	
	printf("%d\n", ans);
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值