洛谷 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 }