树形dp小结

做了好久的树形DP(大雾) ,(noip)csp-s考了好多树形DP
树形DP基本分为这几种

1.最大独立集(没有上司的舞会)

经典树形DP
\(dp[i][0/1]\) 表示i这个点选与不选。
\(dp[u][0] += dp[v][1];\)
\(dp[u][1] += max(dp[v][0],dp[v][1]);\)

2.最小点覆盖(战略游戏)

状态与上面一样
\(dp[u][0] += dp[v][1];\)
\(dp[u][1] += min(dp[v][0],dp[v][1]);\)

3.最小支配集(SDOI保安站岗)

\(dp[i][0/1/2]\) 表示这个点被自己覆盖,被儿子覆盖,被父亲覆盖
\(dp[u][0] += min(dp[v][0],dp[v][1],dp[v][2]);\)
\(dp[u][1] += min(dp[v][1],dp[v][0]);\)
(注:如果u所有的儿子v的dp[v][1] < dp[v][0] 强制选一个最小的dp[v][0]);
\(dp[u][2] += min(dp[v][0],dp[v][1]);\)

   int dt=1e9+7,cnt=0;f[u][1]=pa[u];
    for(int p=last[u];p;p=pre[p])
    {
        int v=other[p];
        if(v==fa)continue;
        dfs(v,u);
    }
    for(int p=last[u];p;p=pre[p])
    {
        int v=other[p];
        if(v==fa)continue;
        f[u][1]+=min(min(f[v][2],f[v][1]),f[v][0]);
        f[u][2]+=min(f[v][1],f[v][0]);
        if(f[v][1]<f[v][0])cnt = 1;
        else dt=min(dt,f[v][1]-f[v][0]);
        f[u][0]+=min(f[v][1],f[v][0]);
    }
    if(cnt==0)
    f[u][0]+=dt;

4.Computer

求树上每个点到最远点的距离
\(dp[i][0/1/2]\) 分别表示子树内最长链,次长链,子树外最长链

inline void dfs1(int x,int fa)
{
    int fis = 0 ,sec = 0;
    for(int p = last[x];p;p = pre[p])
    {
        int v = other[p];
        if(v == fa)continue;
        dfs1(v,x);
        int tmp = dp[v][0] + len[p];
        if(tmp >= fis)
        sec = fis,fis = tmp;
        else if(tmp > sec)
        sec = tmp;
    }
    dp[x][0] = fis;dp[x][1] = sec;
}
inline void dfs2(int x,int fa)
{
    for(int p = last[x];p;p = pre[p])
    {
        int v = other[p];
        if(v == fa)continue;
        if(dp[v][0] == dp[x][0] - len[p])
        dp[v][2] = len[p] + max(dp[x][1],dp[x][2]);
        else 
        dp[v][2] = len[p] + max(dp[x][2],dp[x][0]);
        dfs2(v,x);
    }
}

5.树上背包(选课,有线电视网)

\(dp[i][j]\) 表示子树i中选了j门课
$ dp[u][j] = max(dp[u][j],dp[v][j-k] + dp[u][k]) $

   for(int p=last[u];p;p=pre[p])
    {
        int v=other[p];
        for(int i=m+1;i>=1;i--)
        {
            for(int j=i;j>=1;j--)
            if(f[u][j]>=0&&f[v][i-j]>=0)
            f[u][i]=max(f[u][i],f[u][j]+f[v][i-j]);
        }
    }

复杂度\(O(nm^{2})\)
用dfn序优化之后就是\(O(nm)\)
树上背包基本都是这个套路
特别的像HAOI树上染色一题

     for(int i = min(siz[u],k) ; i >= 0 ;i--)
        {
            for(int j = 0; j <= min(siz[v],i);j++)
            {
                if(f[u][i-j] == -1)continue;
                ll val = (ll)(k-j)*j*len[p] + (ll)(siz[v]-j)*(n-k-siz[v]+j)*len[p];
                f[u][i] = max(f[u][i],f[v][j] + f[u][i-j] + val);
            }
        }

取min的两个位置要特别注意,如果不取min的话复杂度就是错的,这其中的道理不好分析,但这样做完之后,复杂度就是\(O(n^{2})\)

总结

树形DP多数分为\(dp[i][0/1]\),和背包几种类型,主要考虑子树中对当前点的贡献,或者子树外对当前点的贡献,来写出方程。

上一篇:IO流 - 复制文件


下一篇:流的标准处理异常代码1.6版本及其以前