T180034 树形背包 题解(动态规划的序关系)

直接暴力搞树形DP是会爆的,时间复杂度在$O(n^2m)$左右,据说也可以通过某些方法把树形dp优化到$O(nm)$

这里提供另外一种 离 奇 的方法:

首先考虑一棵树的情况,当某一个节点不选时,我们可以将其直接转移到同根的下一个子树,如果某一个节点选择时,那么就可以将其转移到它的所有儿子。

预处理一个dfn(dfs序)和一个next数组就可以完成任务了。

看代码

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 int n,m,w[100001],v[100001],pre[100001],head[1000001],cnt;
 7 struct node
 8 {
 9     int to,next;
10 }a[1000001];
11 void add_edge(int from,int to)
12 {
13     a[++cnt].to=to;
14     a[cnt].next=head[from];
15     head[from]=cnt;
16 }
17 int b[100001],nxt[1000001],fa[1000001],dfn[1000001],times,f[1111][11111][2];//重量为i的背包,当前这个选\没选的价值是多少 
18 void dfs(int u)
19 {
20     b[u]=1;
21     dfn[times]=u;
22     int now=times;
23     times++;
24     for(int i=head[u];i;i=a[i].next)dfs(a[i].to);
25     nxt[now]=times;
26 }
27 int main()
28 {
29     cin>>n>>m;
30     for(int i=1;i<=n;i++)
31     {
32         cin>>w[i]>>v[i]>>pre[i];
33         if(pre[i]!=i)add_edge(pre[i],i),b[i]++;
34     }
35     for(int i=1;i<=n;i++)
36     for(int j=0;j<=m;j++)
37     f[i][j][0]=f[i][j][1]=-1e9;
38     f[0][0][0]=f[0][0][1]=0;
39     times=1;
40     for(int i=1;i<=n;i++)if(!b[i])dfs(i);
41     for(int i=0;i<n;i++)//物品 
42     {
43         for(int j=0;j<=m;j++)//重量 
44         {
45             f[i+1][j+w[dfn[i+1]]][1]=max(f[i+1][j+w[dfn[i+1]]][1],f[i][j][1]+v[dfn[i+1]]);        //
46             f[i+1][j][0]=max(f[i+1][j][0],f[i][j][1]);
47             f[nxt[i]][j+w[dfn[nxt[i]]]][1]=max(f[nxt[i]][j+w[dfn[nxt[i]]]][1],f[i][j][0]+v[dfn[nxt[i]]]);//不选,跳过 
48             f[nxt[i]][j][0]=max(f[nxt[i]][j][0],f[i][j][0]);
49         }
50     }
51     cout<<max(f[n][m][0],f[n][m][1]);
52 
53 }

我要吐了

 

posted @ 2021-07-12 20:06  lei_yu  阅读(33)  评论(0编辑  收藏  举报