树形DP

1.没有上司的舞会

某大学有N个职员,编号为1~N。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri,但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。

 

用f[i][0]表示不选择i点时,i点及其子树能选出的最多人数,f[i][1]表示选择i点时,i点及其子树的最多人数。

第二步:确定状态转移方程

f[i][0] = Σ(max (f[j][0], f[j][1]))

f[i][1] = 1+ Σf[j][0]

(j是i的儿子!!)

边界:f[i][0] = 0, f[i][1] = 1  --------i是叶子节点 

结果为max(f[root][0], f[root][1])

void dfs(long x)
{
    v[x] = 1;
    for (long i=1; i<=n; i++)
    {
        if ((!v[i]) && (fa[i] == x))
        {
            dfs(i);
            f[x][0] += max(f[i][1], f[i][0]);
            f[x][1] += f[i][0];
        }
    }
}

2.二叉苹果树

有一棵苹果树,苹果树的是一棵二叉树,共N个节点,树节点编号为1~N,编号为1的节点为树根,边可理解为树的分枝,每个分支都长着若干个苹果,现在要要求减去若干个分支,保留M个分支,要求这M个分支的苹果数量最多。

第一步:确定状态

f[u][j]表示在以u为根的子树保留j个分支可以得到的最大苹果数量

第二步:确定状态转移方程

F[u][j] = max(f[u][k] + f[v][j – k - 1] + W)

 

v分别是u的儿子,w为u到v边上的苹果数目, k属于[0, j]

 

void dfs(int u)
{
     vis[u]=1;
      int i,v,w,j,k,son=0;
      for(i=head[u];i!=-1;i=e[i].next)
      {
	 v=e[i].ed;w=e[i].w;
	 if(vis[v]==1)continue;
	 dfs(v);
	 for(k=m;k>=1;k--)
	 {
	       for(j=1;j<=k;j++)//在v节点的子树中选择j条边
                         if(f[u][k]<f[u][k-j]+f[v][j-1]+w)
		    f[u][k]=f[u][k-j]+f[v][j-1]+w;
                              //u与v有一条边,所以加上dp[v][j-1]
           }
     }
}		

 

3.Strategic game

一城堡的所有的道路形成一个n个节点的树,如果在一个节点上放上一个士兵,那么和这个节点相连的边就会被看守住,问把所有边看守住最少需要放多少士兵。

第一步:确定状态

f[x][1]以x为根的子树在x上放置的士兵的最少所需的士兵数目

f[x][0]以x为根的子树x上不放置的士兵的最少所需的士兵数目

第二步:确定状态转移方程

f[x][1] =1 + Σ min(f[i][0],f[i][1]) 

// x上放置的士兵,于是它的儿子们可放可不放!

f[x][0] = Σ f[i][1]           

//x上不放置的士兵,它的儿子们都必须放!

(i是x的儿子!!)

结果为min(f[root][0], f[root][1])

void dfs(long x)

{

    v[x] = 1;

    for (long i=0; i<n; i++)

    {

       if ((!v[i]) && (b[x][i]))

        {

            dfs(i);

            f[x][0] += f[i][1];

            f[x][1] += min(f[i][0], f[i][1]);

        }

    }

}

4.Cell Phone Network

给你一棵无向树,问你最少用多少个点可以覆盖掉所有其他的点。(一个点被盖,它自己和与它相邻的点都算被覆盖)

int dp[MAXN][MAXN];   //dp[i][0]表示取i结点时的最小结点数
                      //dp[i][1]表示不取i结点时,i被其儿子覆盖时的最小结点数
                      //dp[i][2]表示不选点i,i被其父亲覆盖时的最小结点数
                      //树形DP状态转移从叶子结点开始向根不断转移,所以用深搜。
                      //搜到叶子结点就结束。
                      //转移的时候可以将结点以下给圈起来。
                      //取与不取,树上背包
int vis[MAXN];
void dfs(int u)
{
    vis[u]=1;
    int i;
    int flag=1,tmp=INF;
    for(i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(!vis[v])                             //额,图的最小支配集吧
        {
            dfs(v);
            dp[u][0]+=min(dp[v][0],min(dp[v][1],dp[v][2]));
            dp[u][2]+=min(dp[v][0],dp[v][1]);   //不取的话,肯定没有父亲这个选项啊
                                                //父亲只有一个,所以不必做过多的讨论
                                                //儿子比较多,可以取所有儿子,但必须选一个儿子
                                                //但选了一个儿子就要做标记
            if(dp[v][0]<=dp[v][1])
           {
              dp[u][1]+=dp[v][0];
              flag=0;
           }
            else
           {
              dp[u][1]+=dp[v][1];
              tmp=min(tmp,dp[v][0]-dp[v][1]);
           }
        }
    }
    if(flag)    //还没有选儿子,加上这个差值,补回一个儿子
    dp[u][1]+=tmp;
    return ;
}

posted @ 2019-07-19 21:20  Snow_in_winer  阅读(101)  评论(0编辑  收藏  举报