【SSL】1605&【洛谷】P2015二叉苹果树

【SSL】1605&【洛谷】P2015二叉苹果树

Time Limit:1000MS Memory Limit:65536K

Description

有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)
这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。
我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树
2 5
\ /
3 4
\ /
1
现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。
给定需要保留的树枝数量,求出最多能留住多少苹果。

Input

第1行2个数,N和Q(1<=Q<= N,1

Output

一个数,最多能留住的苹果的数量。

Sample Input

5 2
1 3 1
1 4 10
2 3 20
3 5 20

Sample Output

21

思路

我们设ma[x][y]为边的值,因为树是双向的,所以要再记录ma[y][x]。
设tree[v,1]为节点v的左子树,tree[v,2]为节点v的右子树,然后我们再递归建树。
定义f[i][j]表示以i为节点的根保留j条边的最大值
f[v][k]=max(f[v][k],(f[tree[v][1]][i]+f[tree[v][2]][k-i-1]+num[v]))
我们枚举i就可以了,用记忆化搜索的形式(dfs)来具体实现。
F[1][Q+1]就是答案。
因为题目中给的是边的权值,而我们在处理时将每条边的权值全赋给其所连的父节点和子节点中的子节点(将关于边的问题转化为关于点的问题),所以最后是Q+1,表示点的数目。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long a[110][110],f[110][110],l[110],r[110],num[110],n;
void maketree(long long v);
void build(long long x,long long y,long long o)
{
    if(o)r[x]=y;
    else l[x]=y;
    num[y]=a[x][y],a[x][y]=a[y][x]=-1,maketree(y);
    return;
}
void maketree(long long x)
{
    bool lr=1;
    for(long long i=1;i<=n;i++)
        if(a[x][i]>-1)
        {
        	lr=!lr,build(x,i,lr);
            if(lr)return;
        }
    return;
}
long long DP(long long x,long long y)
{
	if(f[x][y]>-1)return f[x][y];
	if(y==0)return f[x][y]=0;
	if(!l[x]&&!r[x])return f[x][y]=num[x];
	for(long long k=0;k<y;k++)f[x][y]=max(f[x][y],DP(l[x],k)+DP(r[x],y-k-1)+num[x]);
	return f[x][y];
}
int main()
{
	ios::sync_with_stdio(false);
	long long q,i,j,x,y,t;
	memset(f,-1,sizeof(f)),memset(a,-1,sizeof(a));
	for(cin>>n>>q,i=1;i<n;i++)cin>>x>>y>>t,a[x][y]=a[y][x]=t;
	maketree(1),cout<<DP(1,q+1);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值