【LuoguP1273有线电视网】树形依赖背包

参考论文http://wenku.baidu.com/view/8ab3daef5ef7ba0d4a733b25.html

参考一篇写的很好的博文http://www.cnblogs.com/GXZC/archive/2013/01/13/2858649.html

题目链接http://www.luogu.org/problem/show?pid=1273

首先确定泛化物品的定义:价值随着体积变化的物体,如01背包中的f[i](体积为i时的最大价值最f[i])。

泛化物品+普通物品:

还是01背包,我们是怎么在泛化物品f[i]中加入一个体积为v,价值为w的普通物品的?

f[i]=max(f[i],f[i-v]+w);

O(n)解决。

分析题目:可以看作是叶子节点的v=1,非叶子节点的v=0,w=父亲到该点的费用*(-1)+该点的money。就是一个树形依赖模型,每个节点是一个物品。

设f[i][j]表示dfs序小于等于节点i的所有节点(物品)中,体积为j时的最大价值。

设s为i的一个孩子。

则f[i]管理的范围为红色圈,f[s]管理的范围为蓝色圈。

从上往下(父亲-->孩子)dfs时,先让f[s]=f[i](s继承i的全部信息)

强制让f[s]必须选择s,然后往下对s进行dp。

让f[s]必须选普通物品s是因为选择了s才能选s的孩子:f[s]=f[i]; f[s][j]=max(f[s][j],f[s][j-v]+w);就是说,f[s]=f[i]+物品s

如果不是选择孩子之前必须选择父亲:f[s]=max(f[i],f[i]+物品s)

回溯(孩子-->父亲)时:将f[s]与f[i]合并。

泛化物品的并: 因为两个泛化物品之间存在交集,所以不能同时两者都取,那么我们就需要求 泛化物品的并,对同一体积,我们需要选取两者中价值较大的一者,效率 O(C)。

F[j] = max{ F1[j] , F2[j] } (C>=j>=0)

我的代码以及注释:

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 using namespace std;
 7 
 8 const int N=3010,Inf=(int)1e9;
 9 struct node{
10     int x,y,d,next;
11 }a[2*N];
12 int len,n,m;
13 int first[N],v[N],w[N],f[N][N],cost[N];
14 
15 int maxx(int x,int y){return x>y ? x:y;}
16 
17 void ins(int x,int y,int d)
18 {
19     len++;
20     a[len].x=x;a[len].y=y;a[len].d=d;
21     a[len].next=first[x];first[x]=len;
22 }
23 
24 void init()
25 {
26     len=0;
27     memset(first,0,sizeof(first));
28     for(int i=1;i<=n-m;i++)
29     {
30         int k,x,d;
31         scanf("%d",&k);
32         for(int j=1;j<=k;j++)
33         {
34             scanf("%d%d",&x,&d);
35             ins(i,x,d);ins(x,i,d);
36         }
37     }
38     for(int i=n-m+1;i<=n;i++) scanf("%d",&w[i]);
39     for(int i=1;i<=n-m;i++) v[i]=0;
40     for(int i=n-m+1;i<=n;i++) v[i]=1;
41 }
42 
43 void dfs(int x,int fa)
44 {
45     for(int i=first[x];i;i=a[i].next)
46     {
47         int y=a[i].y;
48         if(y!=fa)
49         {
50             if(y>=n-m+1) cost[y]=-a[i].d+w[y];
51             else cost[y]=-a[i].d;
52             dfs(y,x);
53         }
54     }
55 }
56 
57 void dp(int x,int fa)
58 {
59     for(int i=first[x];i;i=a[i].next)
60     {
61         int y=a[i].y,V=v[y],W=cost[y];
62         if(y==fa) continue;
63         
64         for(int j=0;j<=n;j++) f[y][j]=f[x][j];
65         dp(y,x);
66         
67         // 泛化物品(f[x])加普通物品(节点y:V=v[y],W=cost[y])
68         // 初始化f[y]:f[y][j]=max(f[y][j],f[y][j-V]+W);
69         
70         // 泛化物品(f[x])与泛化物品(f[y])合并(f[x]与f[y]存在交集)
71         // f[x][j]=maxx(f[x][j],f[y][j+V]);
72         for(int j=V;j<=n-V;j++)
73             f[x][j]=maxx(f[x][j],f[y][j-V]+W);//加普通物品y应放在dp(y)后,因为f[y]是表示y可选可不选的,如果先压进去就必须选。
74     }
75 }
76 
77 
78 
79 int main()
80 {
81     freopen("a.in","r",stdin);
82     // freopen("a.out","w",stdout);    
83     scanf("%d%d",&n,&m);
84     init();
85     dfs(1,0);
86     for(int i=1;i<=n;i++) 
87         for(int j=0;j<=n;j++) 
88             if(j==0) f[i][j]=0;
89             else f[i][j]=-Inf;
90     dp(1,0);
91     int ans=0;
92     for(int i=n;i>=1;i--)
93         if(f[1][i]>=0) {printf("%d
",i);return 0;}
94     printf("0
");
95     return 0;
96 }
原文地址:https://www.cnblogs.com/KonjakJuruo/p/5783888.html