题解-luogu-p1273有线电视网

题目链接

背包类树形dp。本题需要运用分组背包模型。

首先定义状态:$f[u][i]$表示以$u$为根的子树上,选择$i$个用户时的最大利润。由于电视公司可能亏本,因此$f$数组应赋极小初值。

可以将选择的用户个数看作背包的容量维度,将获得的利润看作背包的价值维度。可以设计出如下的状态转移:
$$
f[u][i]=\max_{v\in son(u)}{f[u][i-j]+f[v][j]-w}
$$
其中,$v$为$u$的子节点,$w$为这条边的权值。在$u$每个子节点上有许多“物品”,“物品”总数即为以$v$为根的子树上用户的个数;每个“物品”所具有的价值即为其最大利润,即$f[v][j]$。同时不应忽略边权对利润带来的影响。

注意细节处理及边界。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<bits/stdc++.h>
using namespace std;

struct Edge{
int v,w,nxt;
}mem[3005*2];
int head[3005],cnt;
int size[3005];

inline void AddEdge(int u,int v,int w){
mem[++cnt].v=v;
mem[cnt].w=w;
mem[cnt].nxt=head[u];
head[u]=cnt;
}

int N,M;
int leaf[3005];
int f[3005][3005];

inline void dfs(int u){
if(leaf[u]){
f[u][1]=leaf[u];
size[u]=1;
return;
}
for(register int i=head[u];i;i=mem[i].nxt){
int v=mem[i].v,w=mem[i].w;
dfs(v);
size[u]+=size[v];
for(register int j=M;j>=1;--j)
for(register int k=0;k<=min(size[v],j);++k)
f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]-w);
}
}

int main(){
memset(f,0xcf,sizeof(f));
scanf("%d%d",&N,&M);
for(register int i=1;i<=N-M;++i){
int k;
scanf("%d",&k);
for(register int j=1;j<=k;++j){
int a,c;
scanf("%d%d",&a,&c);
AddEdge(i,a,c);
}
}
for(register int i=1;i<=M;++i) scanf("%d",&leaf[N-M+i]);
for(register int i=1;i<=N;++i) f[i][0]=0;
dfs(1);
for(register int i=M;i>=1;--i){
if(f[1][i]>=0){
printf("%d",i);
return 0;
}
}
return 0;
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×