[Vijos1418] 公司聚会


题目描述

dd_engi所在的TIANYI公司要举办一次盛大的公司聚会。可惜的是,由于场地和花费的原因,不可能所有人都参加。现在的任务是拟定参加聚会人员的名单。
TIANYI公司的组织架构可以看做一棵有根多叉树。也就是说,在编号为1~N的所有N名员工中,除了最高管理者(编号为1)以外,每个员工都有且仅有一位直接上司;最高管理者则是这棵多叉树的“根”。这很好理解,对吗?另外,我们保证,员工的编号会大于他的直接上司的编号。
不同的员工对于聚会有着不同的要求,事实上,若邀请第i位员工(编号为i),在聚会中满足他的要求需要花费Ci元。另一方面,不同的员工在聚会中的“兴奋指数”也不同,第i位员工参加聚会的兴奋指数是Ei。
选择参加聚会的人员还有一个限制:为了使参加聚会的员工能够尽量放松,若邀请了某位员工,就不能邀请他的任何一位上司。这里“上司”的定义是这样的:最高管理者没有上司,其余所有员工的直接上司以及直接上司的所有上司都是他的上司。换句话说,某位员工的上司就是树中从他的节点走到根节点的路径上经过的所有节点(包括根结点,但不包括他自身)。
在满足上述限制的前提下,dd_engi希望参加聚会人员的兴奋指数之和最大,但同时他被告知,满足所有参加聚会人员的要求的总花费不得超过M元。那么,参加聚会人员的名单究竟应该怎样确定才最优呢?你需要求出的是最大的总兴奋指数。


输入格式

第一行,两个空格隔开的整数,N与M。
第二行,N-1个整数,分别是第2位至第N位员工的直接上司的编号。
第三行,N个整数,分别是C1、C2……CN。
第四行,N个整数,分别是E1、E2……EN。


输出格式

只需输出一行一个整数,即最大的总兴奋指数。


样例数据

样例输入

10 100
1 2 2 1 4 3 5 6 1
12 53 127 32 164 22 199 10 19 17
-1 0 3 5 7 -2 9 6 8 13

样例输出

27


数据范围

30%的数据满足N<=20。
100%的数据满足1 <= N <= 1024, 0 <= M <= 109, 0 <= Ci <= 1024, -1024 <= Ei <= 1024。


题目分析

在没有钱的限制下就是一个简单的树形动规,向根动规更新f即可解决。
加入了钱的限制就有点麻烦了,首先我们要知道每个儿子结点分多少钱,可以树上做个背包,也可以转为二叉树处理,推荐第二种处理方式代码量较少。


源代码

#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
inline const int Get_Int() {
    int num=0,bj=1;
    char x=getchar();
    while(x<'0'||x>'9') {
        if(x=='-')bj=-1;
        x=getchar();
    }
    while(x>='0'&&x<='9') {
        num=num*10+x-'0';
        x=getchar();
    }
    return num*bj;
}
struct Worker {
    int father,cost,value;
    int leftchild,rightchild;
} a[1055];
int f[1055][115],n,Money;
void TreeDp(int Now) {
    if(Now==0)return;
    TreeDp(a[Now].leftchild);
    TreeDp(a[Now].rightchild);
    for(int i=0; i<=Money; i++)
        for(int j=0; j<=i; j++)
            f[Now][i]=max(f[Now][i],f[a[Now].leftchild][j]+f[a[Now].rightchild][i-j]);
    for(int i=Money; i>=a[Now].cost; i--)
        f[Now][i]=max(f[Now][i],f[a[Now].rightchild][i-a[Now].cost]+a[Now].value);
}
int main() {
    n=Get_Int();
    Money=Get_Int();
    a[1].father=1;
    for(int i=2; i<=n; i++) { //读入顺便将多叉树转为二叉树 
        a[i].father=Get_Int();
        if(a[a[i].father].leftchild==0)a[a[i].father].leftchild=i;
        else {
            int Now=a[a[i].father].leftchild;
            while(a[Now].rightchild)Now=a[Now].rightchild;
            a[Now].rightchild=i;
        }
    }
    for(int i=1; i<=n; i++)a[i].cost=Get_Int();
    for(int i=1; i<=n; i++)a[i].value=Get_Int();
    TreeDp(1);
    printf("%d\n",f[1][Money]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值