【题解】【NHOI2018 初中组】问题 F 博览会(exhibition)

问题 F \text{F} F 博览会 (exhibition) \text{(exhibition)} (exhibition)

题目描述

某市正在举行一场大型博览会,博览会有 n n n 个展馆,每个展馆里有若干个展台。这 n n n 个展馆以及它们之间的道路可以看成一棵二叉树,博览会的出入口设在根节点—— 1 1 1 号展馆,小明将从这里出发乘坐电瓶车到各个展馆参观,并最终回到 1 1 1 号展馆出口。
由于路程差异,乘坐电瓶车往返不同展馆间的费用也有所区别。出发时,小明的乘车卡里余额为 k k k。他现在想知道,若全程都乘坐电瓶车,他最多能参观多少个展台?
说明:只要小明到达了某个展馆,就会参观该展馆内的所有展台,若多次参观同一个展台不重复计算。

输入

输入共 n + 2 n+2 n+2 行:
1 1 1 行为 2 2 2 个整数 n , k n,k n,k,用一个空格隔开,表示展馆个数和小明乘车卡初始余额。
2 2 2 行为 n n n 个数,用一个空格隔开,表示 1 1 1 号至 n n n 号各展馆的展台数目。
接下来 n n n 行,每行 4 4 4 个数,用一个空格隔开;第 i + 2 i+2 i+2 4 4 4 个数分别表示展馆 i i i 左子节点展馆号、到左子节点展馆的费用、右子节点展馆号、到右子节点展馆的费用。如果子节点展馆号为 0 0 0 则表示没有对应的子节点展馆。

输出

输出共 1 1 1 行, 1 1 1 个整数,表示小明最多能参观的展台数量。

样例输入

10 20
2 8 5 1 10 5 9 9 3 5
2 1 3 2
4 8 5 2
6 2 7 2
8 3 9 6
0 0 10 2
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0

样例输出

39

提示

【样例说明】根据样例数据,可以得到如下展馆二叉树示意图(每个圈内标示了展馆号及展台数):
【题解】【NHOI2018 初中组】问题 F 博览会(exhibition)_第1张图片
由图可知,小明沿红色箭号路径,到 1 , 2 , 5 , 3 , 6 , 7 1,2,5,3,6,7 1,2,5,3,6,7这六个展馆参观并返回,往返乘车费用为 18 18 18,参观展台数为 39 39 39,为能够实现的最大值。

【数据范围】

对于 40 % 40\% 40% 的数据: 1 ≤ n ≤ 10 , 1 ≤ k ≤ 20 1\leq n\leq 10,1\leq k\leq 20 1n10,1k20

对于 100 % 100\% 100% 的数据: 1 ≤ n ≤ 50 , 1 ≤ k ≤ 100 1\leq n\leq 50,1\leq k\leq 100 1n50,1k100;所有展馆的展台总数不超过 105 105 105

题解

这题是道树形 D P \mathtt{DP} DP,和 【 CTSC 1997 \text{CTSC 1997} CTSC 1997】选课 这题差不多,只是有边权代价的存在,直接树上 01 01 01背包即可。

f u , j f_{u,j} fu,j 表示以 u u u 为根节点的子树,代价为 j j j,所得到最多能参观的展台数量。
W i W_i Wi 表示 i i i 的父亲到 i i i 的边权。
状态转移方程:
f u , j = max ⁡ { f u , j , f E i . t o , k + f u , j − 2 W E [ i ] . t o − k } f_{u,j}=\max\{f_{u,j},f_{E_i.to,k}+f_{u,j-2W_{E[i].to}}-k\} fu,j=max{fu,j,fEi.to,k+fu,j2WE[i].tok}
另外,这道题有个毒瘤点:
scanf("%d%d%d...")是一起读入,而对于读入 W x W_x Wx W y W_y Wy,要分别读入 x x x W x W_x Wx y y y W y W_y Wy

Code \text{Code} Code

#include 
#include 
#include 
using namespace std;
int n,m,x,y,num_edge,W[51],DP[51][101],head[51],N[51];
struct EDGE {
    int to,next;
}E[51];
void ADD_EDGE(int u,int v) {
    if (!v) return;
    E[++num_edge].to=v;
    E[num_edge].next=head[u];
    head[u]=num_edge;
}
void DFS(int u,int f) {
    for (int i=0;i<=m;i++) DP[u][i]=N[u];
    for (int i=head[u];i;i=E[i].next) {
        if (E[i].to==f) continue;
        DFS(E[i].to,u);
        for (int j=m;j>=2*W[E[i].to];j--)
            for (int k=0;k<=j-2*W[E[i].to];k++)
                DP[u][j]=max(DP[u][j],DP[E[i].to][k]+DP[u][j-2*W[E[i].to]-k]);
    }
}
int main() {
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&N[i]); 
    for (int i=1;i<=n;i++) {
        scanf("%d",&x);
        scanf("%d",&W[x]);
        scanf("%d",&y);
        scanf("%d",&W[y]);
        ADD_EDGE(i,x);
        ADD_EDGE(i,y);
    }
    DFS(1,0);
    return !printf("%d",DP[1][m]);
}

你可能感兴趣的:(树形DP,2020,GPOJ,PJ知识点练习,题解)