bryce1010专题训练——树形DP

树形DP

1.理论部分

给定一颗有N个节点的树(通常是无根树,也就是有N-1条无向边),我们可以任选一个节点作为根节点,从而定义出每个节点的深度和每个子树的根。在树上设计动态规划的算法时,一般就可以以节点从深到浅(子树从小到大)的顺序作为DP的阶段。DP的状态表示中,第一维通常是节点编号(代表以该节点为根的子树)。大多数情况,我们采用递归的方式实现树型动态规划。对于每个节点x,先递归他的每个子节点上进行DP,在回溯时,从子节点想节点x进行状态转移。

2.1 例题 CH5401 没有上司的舞会

Ural大学有N名职员,编号为1~N。他们的关系就像一棵以校长为根的树,父节点就是子节点的直接上司。每个职员有一个快乐指数,用整数 H_i 给出,其中 1≤i≤N。现在要召开一场周年庆宴会,不过,没有职员愿意和直接上司一起参会。在满足这个条件的前提下,主办方希望邀请一部分职员参会,使得所有参会职员的快乐指数总和最大,求这个最大值。

思路:
dp[i][0]表示从根节点到i节点,且i不出席舞会的最大快乐值总和
dp[i][1]表示从根节点到i节点,切i出席舞会的最大快乐值总和。
那么:
dp[i][0]+=max{dp[x][0],dp[x][1]},x为i的儿子节点
dp[i][1]+=dp[x][0]

#include
using namespace std;
#define ll long long

const int MAXN=6e3+100;
int dp[MAXN][3];
//dp[i][0]表示当前节点不出席  dp[i][1]表示当前节点出席
int h[MAXN];
vector<int>G[MAXN];
int flag[MAXN];//用来标记根节点

void solve(int x)
{
    dp[x][0]=0;
    dp[x][1]=h[x];
    for(int i=0;iint y=G[x][i];
        solve(y);
        dp[x][0]+=max(dp[y][0],dp[y][1]);
        dp[x][1]+=dp[y][0];
    }
}

int main()
{
    int n;
    int l,k;
    scanf("%d",&n);
    for(int i=0;iscanf("%d",&h[i]);
    for(int i=0;i1;i++)
    {
        cin>>l>>k;
        G[k-1].push_back(l-1);
        flag[l-1]=1;
    }
    int root;
    for(int i=0;iif(!flag[i])
        {
            root=i;
            break;
        }
    }
    solve(root);
    cin>>l>>k;
    cout<0],dp[root][1])<return 0;
}

2.2 背包类树型DP CH5402 选课

学校实行学分制。每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分。学校开设了 N(N≤300) 门的选修课程,每个学生可选课程的数量 M 是给定的。学生选修了这 M 门课并考核通过就能获得相应的学分。在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其他的一些课程的基础上才能选修。你的任务是为自己确定一个选课方案,使得你能得到的学分最多,并且必须满足先修条件。假定课程之间不存在时间上的冲突。

2.3 二次扫描与换根法 POJ3585 Accumulation Degree

有一个树型的水系,有N-1条河道和N个交叉点组成。连接x与y的河道的容量为c(x,y)。
在流量不超过河道容量的前提下,求那个点作为源点时,真个水系的流量最大,输出这个最大值。N<=2*10^5。

本题是一个“不定根”的树型DP问题。这类问题的特点是,给定一个树型结构,需要以每个节点为根进行一系列统计。我们一般通过两次扫描来求解此类问题:
(1)第一次扫描时,任选一个点为根,在“有根树”上执行一次树型DP,也就是在回溯时发生的、自底向上的状态转移。
(2)第二次扫描时,从刚才选出的根出发,对整棵树执行一次深度优先遍历,在每次递归前进行自顶向下的推倒,计算出“换根”后的解。
——from 算法竞赛进阶指南

你可能感兴趣的:(1.1,1.3.7,动态规划树形DP)