• $Luogu$ $P2014$ 选课


    链接

    背景

    (Luogu) (P2014)

    题意

    给定 (n) 个物品的依赖物品 (k_i)(k_i=0) 表示无依赖物品)及权值 (val_i) ,求选出 (m) 件物品的最大权值和。

    解法

    背包类树形 (dp) 模板。
    考虑把所有依赖关系画在一张图上,显然是一个森林。不妨把每棵树的根都设定一个 (0) 号物品作为依赖物品,则整张图就变成了一棵以 (0) 为根节点的树了。
    按照套路,设 (f_{x,t}) 表示以 (x) 节点为根的子树里选出了 (t) 件物品的最大权值和。假设当前节点 (x) 的儿子为 (y_1,y_2,y_3,cdots cdots,y_p)(p) 个,则转移方程就是 (f_{x,t}=max_limits{ sum_limits{i in [1,p]} t_i=t-1} { sum_limits{i in [1,p]} f_{y_i,t_i} }+val_x)
    考虑变形。这个转移本质上就是从 (p) 组每组都有 (t-1) 个物品的物品集合里每组选出一个物品,一共选出 (t-1) 个。因此采用分组背包的转移即可。
    注意 (x=0) 时并不需要选根节点(即此时可以选 (t) 个物品),于是先不做根节点 (dp) 值的更新,子树都更新完之后再根据 (x) 是否为 (0)(f_{x,t-1}) 转移到 (f_x) 即可。
    另外,由于这是一个分组背包,每件物品只能用一次,所以循环体积的时候要倒序枚举。又因为有体积为 (0) 的物品,所以对于组内物品的枚举也需要倒序。

    细节

    对于给定依赖关系的处理,由于 (k_i=0) 表示无依赖物品,而我们也正好设定一个 (0) 号物品作为依赖物品,于是它的处理与其他的处理没有任何区别,直接在被依赖物品的儿子里加入依赖它的物品即可。

    代码

    $View$ $Code$ ```cpp #include using namespace std; inline int read() { int ret=0,f=1; char ch=getchar(); while('9' sons[305]; void dp(int x) { f[x][0]=0; int siz=sons[x].size(); for(register int i=0;i
  • 相关阅读:
    Java 中的定时任务(一)
    超实用 Git 使用方式介绍
    TCP 建立连接为什么要握 3 次手?
    OSI、TCP、IP、UDP 这些都是啥??
    Java 中线程安全问题
    PlantUML——3.Graphviz的安装
    PlantUML——2.中文乱码及解决
    PlantUML——1.Hello
    maven实战系列
    NGUI优化之Drawcall
  • 原文地址:https://www.cnblogs.com/Peter0701/p/11837616.html
Copyright © 2020-2023  润新知