洛谷 1273 有线电视网

我一直觉得这题的题解写的有问题

大多数题解都说他是分组背包

但是我觉得这并不是分组背包,与分组背包有差别

咱们先看看题

题意就是要在不亏本的情况下,使最多的叶子节点看到比赛

咱们来想想这题的思路

这是典型的树形dp

也是我做的第一道树形dp

我们考虑一下dp方程

我们设dp[i][j],表示以i为根节点,满足j个客户所能获得的最大值

如何转移?

dp[i][j]=max(dp[i][j-k]+dp[x][k],dp[i][j]) x为i的儿子节点

在dfs时就可以计算出每个节的孩子数

看看代码,有注释

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <vector>
 6 #include <cstring>
 7 using namespace std;
 8 const int N=3005;
 9 struct node
10 {
11     int x,v;
12     node(int xx,int vv)
13     {
14         x=xx,v=vv;
15     }
16 };
17 vector <node> g[N];
18 int n,m,k,x,y,d[N],dp[N][N];
19 int dfs(int x)
20 {
21     if(x>n-m)
22     {
23         dp[x][1]=d[x];//到根节点这个点的值肯定为他自身的值
24         return 1;
25     }
26     int sum=0,t;
27     for(int i=0;i<g[x].size();i++)
28     {
29         t=dfs(g[x][i].x);
30         sum+=t;//sum为他的总孩子节点个数
31         for(int j=sum;j>0;j--)
32         {
33             for(int kk=1;kk<=t;kk++)//t为他的孩子的孩子节点个数
34             {
35                 if(j-kk>=0)
36                     dp[x][j]=max(dp[x][j],dp[x][j-kk]+dp[g[x][i].x][kk]-g[x][i].v);//dp过程
37             }
38         }
39     }
40     return sum;
41 }
42 int main()
43 {
44     scanf("%d %d",&n,&m);
45     int v=n-m;
46     for(int i=1;i<=v;i++)
47     {
48         scanf("%d",&k);
49         for(int j=1;j<=k;j++)
50         {
51             scanf("%d %d",&x,&y);
52             g[i].push_back(node(x,y));//加边
53         }
54     }
55     for(int i=n-m+1;i<=n;i++)
56         scanf("%d",&d[i]);
57     memset(dp,~0x3f,sizeof(dp));
58     for(int i=1;i<=n;i++)
59         dp[i][0]=0;//初始化,每个节点选0个孩子肯定是0
60     dfs(1);
61     for(int i=m;i>=1;i--)
62     {
63         if(dp[1][i]>=0)//大于零就可以选
64         {
65             printf("%d
",i);
66             break;
67         }
68     }
69     return 0;
70 }

这题就解决了

原文地址:https://www.cnblogs.com/wzrdl/p/9801806.html