洛谷2014选课(树型dp)
题目:https://www.luogu.org/problemnew/show/P2014
千万注意遍历 j 和 k 的边界!
0点很好用。
siz很好用。
#include<iostream> #include<cstdio> using namespace std; int n,m,a[305],f[305][305],cnt,l[305],siz[305],x; struct Node{ int to,next; }edge[305]; bool b[305]; void add(int x,int y) { cnt++; edge[cnt].next=l[x]; edge[cnt].to=y; l[x]=cnt; } void dfs(int cur) { if(b[cur])return; b[cur]=1; f[cur][1]=a[cur]; siz[cur]++; for(int i=l[cur];i;i=edge[i].next) { int v=edge[i].to; dfs(v); for(int j=min(m,siz[cur]+siz[v]);j>1;j--)////// for(int k=max(j-siz[cur],0);k<=siz[v]&&k<j;k++)///// f[cur][j]=max(f[cur][j],f[cur][j-k]+f[v][k]); siz[cur]+=siz[v]; } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d%d",&x,&a[i]); add(x,i); } for(int i=l[0];i;i=edge[i].next) { int v=edge[i].to; dfs(v); for(int j=min(m,siz[0]+siz[v]);j;j--)///// for(int k=max(j-siz[0],0);k<=siz[v]&&k<=j;k++)///// f[0][j]=max(f[0][j],f[0][j-k]+f[v][k]); siz[0]+=siz[v]; } printf("%d",f[0][m]); return 0; }
后续的故事:
考试的时候考出了原题,然后爆0……
关键在于倒序。首先一定不能忘记倒序,因为实质上是省了一维的;
而且这个倒序可以触及到1(甚至0也可以,因为有下面的k<j),这样的话在我们已有的所有 d[ ][ ] 中就已经包含了根节点的值了(别忘了提前赋 1 的值)。
siz的更新和 j,k 边界仍然需要注意。