这里会显示出您选择的修订版和当前版本之间的差别。
| 两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
|
2020-2021:teams:legal_string:jxm2001:动态规划_3 [2021/01/14 20:20] jxm2001 |
2020-2021:teams:legal_string:jxm2001:动态规划_3 [2021/01/14 20:28] (当前版本) jxm2001 |
||
|---|---|---|---|
| 行 20: | 行 20: | ||
| \text{dp}(i,j)=\max(\text{dp}(i-sz_i,j),\text{dp}(i-1,j-w_i)+v_i) | \text{dp}(i,j)=\max(\text{dp}(i-sz_i,j),\text{dp}(i-1,j-w_i)+v_i) | ||
| $$ | $$ | ||
| + | |||
| + | 时间复杂度 $O(nm)$。 | ||
| <code cpp> | <code cpp> | ||
| 行 51: | 行 53: | ||
| === 题解 === | === 题解 === | ||
| + | 设 $dp(i,j)$ 表示以 $i$ 为根节点的子树中选取 $j$ 个叶子结点的最大盈利,不难得出状态转移方程。 | ||
| + | 考虑以类似枚举 $\text{LCA}$ 的方式合并子节点结果,可以 $O(n^2)$ 完成所有状态转移。最后查询满足 $dp(1,i)\ge 0$ 的最大的 $i$ 即可。 | ||
| + | |||
| + | <hidden 查看代码> | ||
| + | <code cpp> | ||
| + | const int MAXN=3e3+5,Inf=1e9; | ||
| + | LL dp[MAXN][MAXN]; | ||
| + | int head[MAXN],edge_cnt; | ||
| + | struct Edge{ | ||
| + | int to,w,next; | ||
| + | }edge[MAXN]; | ||
| + | void Insert(int u,int v,int w){ | ||
| + | edge[++edge_cnt]=Edge{v,w,head[u]}; | ||
| + | head[u]=edge_cnt; | ||
| + | } | ||
| + | int sz[MAXN]; | ||
| + | void dfs(int u){ | ||
| + | for(int i=head[u];i;i=edge[i].next){ | ||
| + | int v=edge[i].to; | ||
| + | dfs(v); | ||
| + | for(int j=sz[u];j>=0;j--)_rep(k,1,sz[v]) | ||
| + | dp[u][j+k]=max(dp[u][j+k],dp[u][j]+dp[v][k]-edge[i].w); | ||
| + | sz[u]+=sz[v]; | ||
| + | } | ||
| + | } | ||
| + | int main() | ||
| + | { | ||
| + | int n=read_int(),m=read_int(); | ||
| + | _rep(i,1,n)_rep(j,1,n)dp[i][j]=-Inf; | ||
| + | _rep(i,1,n-m){ | ||
| + | int k=read_int(); | ||
| + | while(k--){ | ||
| + | int v=read_int(),w=read_int(); | ||
| + | Insert(i,v,w); | ||
| + | } | ||
| + | } | ||
| + | _rep(i,n-m+1,n)dp[i][1]=read_int(),sz[i]=1; | ||
| + | dfs(1); | ||
| + | for(int i=m;i>=0;i--){ | ||
| + | if(dp[1][i]>=0){ | ||
| + | cout<<i; | ||
| + | break; | ||
| + | } | ||
| + | } | ||
| + | return 0; | ||
| + | } | ||
| + | </code> | ||
| + | </hidden> | ||