【DP专题】——洛谷P1273有线电视网

树形dp板子。

传送门:GO

 


 

  用dp[i][j]表示以i为根的子树中选取j个观众能赚到的最多钱(经典模型)。

  那么,dp[i][j+k]=max(dp[son[i]][k]+dp[i][j]-w,dp[i][j+k]),意思是说,如果在子树中选了k个观众,那么答案就是(当前只选了j个的情况+选子树中k个观众的情况-这一段代价)。

  确定一下枚举上界:初步估计,每一次选择观众量就是当前子树所含观众量,所以将dfs设计成返回子树所含观众量的类型,就可以处理枚举上界了。

  对于节点u,外层枚举上界就是u的所有子树的前缀(需动态更新),内层上界就是当前子树大小。

  考虑到转移式子,少的观众会对多的观众造成影响,所以倒序枚举,从多到少,就不会造成多余影响了。

  其余的见代码吧。

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int read(){
 4     int x=0,f=1;
 5     char c=getchar();
 6     while(!isdigit(c)){
 7         if(c=='-') f=-1;
 8         c=getchar();
 9     }
10     while(isdigit(c)){
11         x=x*10+c-'0';
12         c=getchar();
13     }
14     return x*f;
15 }
16 const int N=3010;
17 int n,m,cnt;
18 int head[N<<1];
19 int mon[N],f[N][N];
20 struct edge{int to,next,w;}e[N<<1];
21 void addedge(int from,int to,int w){e[++cnt]=(edge){to,head[from],w};head[from]=cnt;}
22 int dfs(int u){
23     if(u>n-m){
24         f[u][1]=mon[u];
25         return 1;
26     }
27     int upz=0;
28     for(int i=head[u];i;i=e[i].next){
29         int v=e[i].to,w=e[i].w;
30         int now=dfs(v);
31         for(int j=upz;j>=0;j--){
32             for(int k=0;k<=now;k++){
33                 f[u][j+k]=max(f[u][j+k],f[u][j]+f[v][k]-w);
34             }
35         }
36         upz+=now;
37     }
38     return upz;
39 }
40 int main(){
41     n=read();m=read();
42     for(int i=1,k;i<=n-m;i++){
43         k=read();
44         for(int j=1;j<=k;j++){
45             int x=read(),y=read();
46             addedge(i,x,y);
47         }
48     }
49     for(int i=n-m+1;i<=n;i++) mon[i]=read();
50     memset(f,-0x3f,sizeof(f));
51     for(int i=1;i<=n;i++) f[i][0]=0;
52     dfs(1);
53     for(int i=m;i>0;i--){
54         if(f[1][i]>=0){
55             printf("%d",i);
56             return 0;
57         }
58     }
59     return 0;
60 } 

 

posted @ 2019-09-24 20:28  Nelson992770019  阅读(144)  评论(0编辑  收藏  举报