jzoj 5788 餐馆

8 篇文章 0 订阅

题目

这里写图片描述这里写图片描述这里写图片描述


题解

–本宝看出来了呀,开出是树形dp来了呀,就是没写过,bl了呀(QAQ)
至少明白了树形dp就是从儿子转移到父亲的合并呀
这道题就是设嘛:
f[i][j][0] : 以i为树根的子树,花费j分钟,并且回到i节点的最大价值
f[i][j][1] : 以i为树根的子树,花费j分钟,并且不需要回到i节点的最大价值

然后转移就有三种情况:
1. 从已经跑过的子树,并回到根,再跑目前子树,并回到根
f[x][j+k+2][0]=max(f[x][j+k+2][0],f[x][j][0]+f[v][k][0]);
2. 从已经跑过的子树,并回到根,再跑目前子树,不需要回到根
f[x][j+k+1][1]=max(f[x][j+k+1][1],f[x][j][0]+f[v][k][1]);
3. 从目前子树跑,并回到根,再跑已经跑过的子树,不需要回到根
f[x][j+k+2][1]=max(f[x][j+k+2][1],f[x][j][1]+f[v][k][0]);

我们只需要对每个儿子都枚举目前耗时j,和希望在该子树上花的时间k
如上dp就好啦

剩下就是边界情况了:
就是对每一个dp跑完了的树根,都再决策一次是否要加上这个根的价值(注意不需要回到根的情况也要加,因为他肯定是经过了根的)

祝AC


代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN=505;

int n,m;
int a[MAXN];
vector<int>g[MAXN];
bool isv[MAXN];
int f[MAXN][MAXN][2];

void dp(int x){
    isv[x]=1;
    for(int i=0;i<g[x].size();i++){
        int v=g[x][i];
        if(isv[v])
            continue;
        dp(v);
        for(int j=m;j>=0;j--){
            for(int k=m;k>=0;k--){
                if(j+k+1<=m)
                    f[x][j+k+1][1]=max(f[x][j+k+1][1],f[x][j][0]+f[v][k][1]);
                if(j+k+2<=m){
                    f[x][j+k+2][0]=max(f[x][j+k+2][0],f[x][j][0]+f[v][k][0]);
                    f[x][j+k+2][1]=max(f[x][j+k+2][1],f[x][j][1]+f[v][k][0]);
                }
            }
        }
    }
    for(int i=m;i>=1;i--){
        f[x][i][0]=max(f[x][i][0],f[x][i-1][0]+a[x]);
        f[x][i][1]=max(f[x][i][1],f[x][i-1][1]+a[x]);
    }
}

int main(){
    freopen("dostavljac.in","r",stdin);
    freopen("dostavljac.out","w",stdout);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dp(1);
    cout<<max(f[1][m][0],f[1][m][1]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值