题目链接:
没有上司的舞会 - 洛谷https://www.luogu.com.cn/problem/P1352
思路:
树形dp经典题,由于是第一次做这类题,记录一下。
首先看到题目,肯定是个dp题,但是这是个树形的dp,所以dp的顺序没那么好确定。首先dp是自底向上的,所以肯定得想办法找底,然后返回结果是看树根的,所以还得想办法找顶。对此我想到了两种方法:第一种是拓扑排序,最先入队列的最先dp,最后入队列的是根结点。第二种是dfs,找到根结点之后,自顶向下dfs,到树底之后再自底向上dp。
总的来说,两种方法里dfs更好写,看了别人的代码,大多也是用dfs。
代码:
1.dfs
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 6e3+5;
int n; //people count
int happy[maxn]; //happy number
vector<int> g[maxn]; //edge(上司->下属)
bool isRoot[maxn];
int dp[maxn][2];
void dfs(int u){
dp[u][0] = 0;
dp[u][1] = happy[u];
for(int v : g[u]){
dfs(v);
dp[u][0] += max(dp[v][0], dp[v][1]);
dp[u][1] += dp[v][0];
}
}
int main(){
cin >> n;
for(int i=1; i<=n; i++){
cin >> happy[i];
}
fill(isRoot+1, isRoot+n+1, true);
for(int i=1; i<=n-1; i++){
int a, b;
cin >> a >> b;
g[b].push_back(a);
isRoot[a] = false;
}
int root;
for(int i=1; i<=n; i++){
if(isRoot[i]) {root = i; break;}
}
dfs(root);
cout << max(dp[root][0], dp[root][1]) << endl;
}
2.拓扑排序
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 6e3+5;
int n; //people count
int happy[maxn]; //happy number
vector<int> g[maxn]; //edge(上司->下属)
int out[maxn]; //出度数组
int fa[maxn];
int dp[maxn][2];
int main(){
cin >> n;
for(int i=1; i<=n; i++){
cin >> happy[i];
}
for(int i=1; i<=n-1; i++){
int a, b;
cin >> a >> b;
g[b].push_back(a);
fa[a] = b;
out[b]++;
}
queue<int> q;
for(int i=1; i<=n ;i++){
if(out[i] == 0) q.push(i);
}
int root; //用来记录最后一个进入队列的点,它必定是根结点
while(q.size()){
int u = q.front(); q.pop();
root = u;
dp[u][0] = 0;
dp[u][1] = happy[u];
for(int v : g[u]){
dp[u][0] += max(dp[v][0], dp[v][1]);
dp[u][1] += dp[v][0];
}
if(--out[fa[u]] == 0) q.push(fa[u]);
}
cout << max(dp[root][0], dp[root][1]) << endl;
}