Luogu P1273 有线电视网

最近写DP写得比较多了

但是POJ上的题目太傻比了而且不想看英文的题面,然后就在Luogu的试炼场里找了一个DP EX专题写了一下(大概3days吧,一天一题差不多)

这是一道比较简单的DP

话说树形DP一般都可以直接上暴力DP,可能是因为树的复杂度一般都比较玄学

这里是一个类树形背包的板子,我们设:

  • s[i][j]表示以i为根的子树最多有多少个叶节点

  • f[i][j]表示已i点为根的子树中选j个叶节点最大利润(可以为负)

然后转移的时候从i点的所有子树转移一下即可

各选几个点直接枚举一下即可(注意要倒序

这种树上的DP写记忆化搜索会比较舒服(其实这应该也不算记搜)

CODE

#include<cstdio>
#include<cstring>
using namespace std;
const int N=3005;
struct data
{
    int to,next,v;
}e[N];
int head[N],f[N][N],c[N],s[N],n,m,x,y,z,cnt,root=1;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch=tc();
    while (ch<'0'||ch>'9') ch=tc();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline void add(int x,int y,int z)
{
    e[++cnt].to=y; e[cnt].v=z; e[cnt].next=head[x]; head[x]=cnt;
}
inline int max(int a,int b)
{
    return a>b?a:b;
}
inline void DP(int now)
{
    register int i,j,k;
    f[now][0]=0;
    if (head[now]==-1)
    {
        s[now]=1; f[now][1]=c[now];
        return;
    }
    for (i=head[now];i!=-1;i=e[i].next)
    {
        DP(e[i].to);
        for (j=s[now];j>=0;--j)
        for (k=s[e[i].to];k>=0;--k)
        f[now][j+k]=max(f[now][j+k],f[now][j]+f[e[i].to][k]-e[i].v);
        s[now]+=s[e[i].to];
    }
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register int i,j;
    read(n); read(m);
    memset(head,-1,sizeof(head));
    memset(e,-1,sizeof(e));
    memset(f,163,sizeof(f));
    for (i=1;i<=n-m;++i)
    for (read(x),j=1;j<=x;++j)
    read(y),read(z),add(i,y,z);
    for (i=n-m+1;i<=n;++i)
    read(c[i]);
    DP(root);
    for (i=m;i>=0;--i)
    if (f[1][i]>=0) { printf("%d",i); break; }
    return 0;
}
原文地址:https://www.cnblogs.com/cjjsb/p/9016330.html