洛谷 P1273 有线电视网(树形dp,区间dp)

传送门


解题思路

自己一开始做的时候知道是dp,但是设计的状态总是有后效性的(看来还是做题少了),看完题解才恍然大悟。

设dp[u][i][j]表示以u为根的子树,选取前i个儿子,用户数为j的最大利润,用类似区间dp的性质,dp[u][i][j]可以从dp[u][i-1][j-k]+dp[v][size[v]][k]-e[cnt].value转移过来。

看一下数据范围和状态转移方程,可以发现i这一维可以用滚动数组优化,这样空间就过了。

注意点:读入w[i]时 循环变量i从1~m,对应的是w[n-m+i]。

AC代码

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int maxn=3005;
 6 int n,m,p[maxn],dp[2][maxn][maxn],kk[maxn],cnt,w[maxn];
 7 struct node{
 8     int v,next,value;
 9 }e[maxn];
10 void insert(int u,int v,int value){
11     cnt++;
12     e[cnt].v=v;
13     e[cnt].next=p[u];
14     e[cnt].value=value;
15     p[u]=cnt;
16 }
17 int dfs(int u){
18     dp[0][u][0]=dp[1][u][0]=0;
19     if(p[u]==-1){
20         dp[0][u][1]=dp[1][u][1]=w[u];
21         return 1;
22     }
23     int sum=0,t=0;
24     for(int i=p[u];i!=-1;i=e[i].next){
25         int v=e[i].v;
26         int kk=dfs(v);
27         for(int j=1;j<=m;j++){
28             dp[t][u][j]=dp[t^1][u][j];
29             for(int k=1;k<=min(j,kk);k++){
30                 dp[t][u][j]=max(dp[t][u][j],dp[t^1][u][j-k]+max(dp[0][v][k],dp[1][v][k])-e[i].value);
31             }
32         }
33         sum+=kk;
34         t=t^1;
35     }
36     return sum;
37 }
38 int main(){
39     memset(dp,-0x3f,sizeof(dp));
40     memset(p,-1,sizeof(p));
41     scanf("%d%d",&n,&m);
42     for(int i=1;i<=n-m;i++){
43         int v,value,kk;
44         scanf("%d",&kk);
45         for(int j=1;j<=kk;j++){
46             scanf("%d%d",&v,&value);
47             insert(i,v,value);
48         }
49     }
50     for(int i=1;i<=m;i++) scanf("%d",&w[n-m+i]);
51     dfs(1);
52     for(int i=m;i>=0;i--){
53         if(dp[0][1][i]>=0||dp[1][1][i]>=0){
54             cout<<i<<endl;
55             break;
56         }
57     }
58     return 0;
59 } 

 

posted @ 2020-10-26 23:25  尹昱钦  阅读(97)  评论(0编辑  收藏  举报