题意:
n-m+1~n的点都为用户,用户与用户的点之间没有父子关系,其余的点不是用户,用户有权值
点与点间需要消耗边权值才能搭建这一条边,从根节点需要经过边才能到达用户并获得点权值
求在不亏损的情况下,能达到的用户最大为多少?
树形背包dp:
f[u][j]表示以u为根节点到达j个用户的最大利润(利润可能为负,代表亏损)
考虑转移:
对于用户来说f[u][1]=点权值
对非用户点来说,初始时f[u][0]=0(f[u][1]以及其他都为-inf,因为他本身不是用户点)
j的最大值为它的子树包含的用户点的数目(注意不是点的数目而是用户点的数目)
f[u][j] = max(f[u][j],f[u][j - k] + f[too][k] - val[i]);
最后求使f[1][j]大于等于0的最大的j值即可
时间复杂度:O(nm^2)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 int to[6005],head[6005],nxt[6005],cnt,val[6005]; 6 int a[3005],siz[3005],f[3005][3005]; 7 int n,m; 8 void add(int u,int v,int w) 9 { 10 to[++cnt] = v; 11 nxt[cnt] = head[u]; 12 head[u] = cnt; 13 val[cnt] = w; 14 } 15 void dfs(int u,int fa) 16 { 17 f[u][0] = 0; 18 if(u >= n - m + 1) 19 { 20 f[u][1] = a[u]; 21 siz[u] = 1; 22 } 23 for(int i = head[u];i;i=nxt[i]) 24 { 25 int too = to[i]; 26 if(too == fa)continue; 27 dfs(too,u); 28 siz[u] += siz[too]; 29 for(int j = siz[u];j >= 1;j --) 30 { 31 for(int k = 1;k <= min(j,siz[too]);k ++) 32 { 33 f[u][j] = max(f[u][j],f[u][j - k] + f[too][k] - val[i]); 34 35 // printf("f[%d][%d]==%d + f[%d][%d]==%d - %d = %d ",u,j - k,f[u][j-k],too,k,f[too][k],val[i],f[u][j]); 36 } 37 } 38 } 39 } 40 int main() 41 { 42 scanf("%d%d",&n,&m); 43 for(int i = 1,k;i <= n - m;i ++) 44 { 45 scanf("%d",&k); 46 for(int j = 1,v,w;j <= k;j ++) 47 { 48 scanf("%d%d",&v,&w); 49 add(i,v,w); 50 } 51 } 52 for(int i = n - m + 1;i <= n;i ++) 53 { 54 scanf("%d",&a[i]); 55 } 56 memset(f,-0x3f,sizeof(f)); 57 dfs(1,0); 58 for(int i = m;i >= 0;i --) 59 { 60 // printf("f[1][%d]=%d ",i,f[1][i]); 61 if(f[1][i] >= 0) 62 { 63 printf("%d",i); 64 break; 65 } 66 } 67 return 0; 68 }