P3698-[CQOI2017]小Q的棋盘【树形dp】

正题

题目链接:https://www.luogu.com.cn/problem/P3698


题目大意

n n 个点的树,求从 1 1 出发走 k k 步最多能到多少个节点。(重复走不算)


解题思路

做法与树形背包类似,但是需要注意的是最后不需要返回原点。

f i , j , 0 / 1 f_{i,j,0/1} 表示第 i i 个点的子树中走 j j 步,需要/不需要返回 i i 号点的最多点数。

然后有转移方程
f x , i , 0 = m a x { f x , i j 2 , 0 + f y , j , 0 } + 1 f_{x,i,0}=max\{f_{x,i-j-2,0}+f_{y,j,0}\}+1
f x , i , 1 = m a x { f x , i j 2 , 1 + f y , j , 0 , f x , i j 1 , 0 + f y , j , 1 } + 1 f_{x,i,1}=max\{f_{x,i-j-2,1}+f_{y,j,0},f_{x,i-j-1,0}+f_{y,j,1}\}+1

时间复杂度 : O ( n 3 ) :O(n^3)


c o d e code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110;
struct node{
	int to,next;
}a[N*2];
int n,k,f[N][N][2],ls[N],tot;
void addl(int x,int y)
{
	a[++tot].to=y;
	a[tot].next=ls[x];
	ls[x]=tot;
}
void dp(int x,int fa)
{
	for(int i=ls[x];i;i=a[i].next)
	{
		int y=a[i].to;
		if(y==fa) continue;
		dp(y,x);
		for(int j=k;j>=0;j--)
			for(int z=0;z<=j;z++){
				if(j-z-2>=0)f[x][j][0]=max(f[x][j][0],f[y][z][0]+f[x][j-z-2][0]);
				if(j-z-2>=0)f[x][j][1]=max(f[x][j][1],f[y][z][0]+f[x][j-z-2][1]);
				if(j-z-1>=0)f[x][j][1]=max(f[x][j][1],f[y][z][1]+f[x][j-z-1][0]);
			}
	}
	for(int j=0;j<=k;j++)
		f[x][j][0]++,f[x][j][1]++;
}
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		x++;y++;
		addl(x,y);addl(y,x);
	}
	dp(1,1);
	printf("%d",f[1][k][1]);
}
发布了867 篇原创文章 · 获赞 55 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/103842267