洛谷P1352—没有上司的舞会(树形DP)

树形DP就是在树的基础上做动态规划
树形DP有两个方向:
1.叶->根,回溯是从叶子结点往上更新
2.根->叶,往往是在从叶往根dfs一遍之后(相当于预处理),再重新往下获取最后的答案。

题意

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

思路

这道题是树形DP中的典中典,从叶子往根回溯。
建树:根据上司和员工的从属关系建立一个有向树
每个结点有一个价值,父节点和子节点不能同时选。
每个结点都是选和不选两种选择。
状态转移数组:

dp[u][0]为u结点不去,获得的子树的最大价值
dp[u][1]为u结点去,获得的子树d饿最大价值

可以看出一共有三种情况和对应的状态转移方程
1.上司去,员工不去dp[u][1] = r[u] + dp[v][0]
2.上司不去,员工去dp[u][0] = dp[v][1]
3.上司不去,员工不去dp[u][0] = dp[v][0]
第二种和第三种情况合并dp[u][0] = max{dp[v][1],dp[u][0]}

对根节点进行dfs,最终答案为max{dp[root][0],dp[root][1]}

#include <iostream>
#include<algorithm>
#include<cmath>
using namespace std;

const int MAX_N = 6010; //最多结点数
int tot;    //标记边的序号
int head[MAX_N],nxt[MAX_N],ver[MAX_N];  //建树要用到的数组
int r[MAX_N],flag[MAX_N];   //r为价值,flag标记u是否有上司
int dp[MAX_N][2];
void addedge(int u,int v){  //根据邻接表建树的过程
    ver[++tot] = v;     //tot条边指向的点为v
    nxt[tot] = head[u]; //nxt保存以u为始点的下一条边的序号
    head[u] = tot;      //head[u]保存以u为始点的边的序号
}

void dfs(int u){
    dp[u][0] = 0;   //初始化u不去得到的dp
    dp[u][1] = r[u];    //初始化u去得到的dp
    for(int i = head[u]; i;i = nxt[i]){    //搜索u的所有孩子
        int v = ver[i];
        dfs(v);
        dp[u][1] += dp[v][0];
        dp[u][0] += max(dp[v][0],dp[v][1]);
    }
}
int main(){
	int n;
    cin >> n;
	for(int i = 1;i <= n;i++)
        cin >> r[i];
    int u,v;
    for(int i = 1;i <= n-1;i++){
        cin >> v >> u;
        addedge(u,v);
        flag[v] = 1;    //v有上司
    }
    int root;
    for(int i = 1;i <= n;i++){  //寻找根节点
        if(!flag[i]){
            root = i;
            break;
        }
    }
    dfs(root);  //从根节点开始dfs
    cout << max(dp[root][0],dp[root][1]) << endl;   //根节点也有选和不选两种选择
	return 0;
}

上一篇:P4195 【模板】扩展BSGS


下一篇:2019.7.14 义乌模拟赛 T3 set