【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\n",i);return 0;}
94 printf("0\n");
95 return 0;
96 }