[洛谷P2015] 二叉苹果树

3 篇文章 0 订阅

题目描述

有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)

这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。

我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树

2   5
 \ / 
  3   4
   \ /
    1

现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。

给定需要保留的树枝数量,求出最多能留住多少苹果。

输入输出格式

输入格式:

 

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

N表示树的结点数,Q表示要保留的树枝数量。接下来N-1行描述树枝的信息。

每行3个整数,前两个是它连接的结点的编号。第3个数是这根树枝上苹果的数量。

每根树枝上的苹果不超过30000个。

 

输出格式:

 

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

 

输入输出样例

输入样例#1:

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

输出样例#1:

21

比较基础的树形dp,以每个节点及节点对应剪枝数为状态,容易得出方程

f\left ( u,i \right )=max\left \{ f\left ( v,j \right )+f\left ( u,i-j-1 \right )+w\left ( u,v \right ) \right \}

其中 u,v 表示当前节点及子节点,i j 表示当前状态还有多少枝条可剪

由于核心部分由递推实现,j 的更新顺序应该从大到小,防止覆盖

代码

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n,q;
const int N=101;
int d[N][N];
struct edge
{
    int to; int w;
    edge(int v,int s):to(v),w(s){}
};
vector<edge> G[N];
void dp(int u,int fa)
{
    for(int i=0;i<G[u].size();i++)
    {
        edge v=G[u][i]; if(v.to==fa) continue;
        dp(v.to,u);
        for(int i=q;i>=0;i--)
            for(int j=0;j<i;j++)
                d[u][i]=max(d[u][i],d[u][j]+d[v.to][i-j-1]+v.w);
        }
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<n;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        G[u].push_back(edge(v,w));
        G[v].push_back(edge(u,w));
        }
    dp(1,-1);
    printf("%d\n",d[1][q]);
    return 0;
}

 这是我第一次写的,很没有条理的搜索,un记录了每个节点位于限制状态下的剪枝状态,事实上没必要考虑限制,因为更新过程是从顶到底层层搜索,不会访问到不需要的部分

不过这还是有优点的,这是一次漂亮的状态压缩啊!(滑稽)

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n,q,tot=0;
const int N=101;
int d[N][N];
struct edge
{
    int to; int w;
    edge(int v,int s):to(v),w(s){}
};
vector<edge> G[N];
int dp(int u,int fa,int un)
{
    int &ans=d[u][un];
    if(tot>=un) return ans=0;//边界
    if(ans) return ans;//记忆化
    edge x=edge(0,0); edge y=edge(0,0); 
    for(int i=0;i<G[u].size();i++)
    {
        edge v=G[u][i]; if(v.to == fa) continue;
        if(x.to==0) x=v; else y=v;
        }
    int maxx=0;
    if(x.to!=0) {tot++; maxx=max(maxx,dp(x.to,u,un)+x.w); tot--;}
    if(y.to!=0) {tot++; maxx=max(maxx,dp(y.to,u,un)+y.w); tot--;}
    if(x.to!=0 && y.to !=0 && tot+2 <= un) for(int i=tot;i<=un;i++)
    {
        int ans1=0,ans2=0;
		tot+=1; if(tot <= i) ans1=dp(x.to,u,i)+x.w;tot-=1;
		tot+=1; if(tot <= un-i+tot-1) ans2=dp(y.to,u,un-i+tot-1)+y.w;tot-=1;
		maxx=max(maxx,ans1+ans2);
        }
    return ans=maxx;
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<n;i++)
    {
        int u,v,va;
        scanf("%d%d%d",&u,&v,&va);
        G[u].push_back(edge(v,va));
        G[v].push_back(edge(u,va));
        }
    int ans=dp(1,-1,q);
    printf("%d\n",ans);
    return 0;
}

衍生题目:选课

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值