洛谷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 边界仍然需要注意。

原文地址:https://www.cnblogs.com/Narh/p/8443423.html