HDU 1520 Anniversary party 树状dp
题意
大体意思说,一个大学要开一个party,大学里会有很多人回去参加,但是并不是很多人都愿意去,因为有可能遇到自己的直接上司。本来是一个放松的场所,遇到自己的直接boss可真的不大放得开,遇到上上级boss还可以,这就给组织这个活动的部门一个难题,每个人都有自己的开心值,我们该怎么做才能既避免这种尴尬,又能使得参加人的开心值总和最大呢?
先给一个n
表示大学里面人的个数,编号从1
开始,然后是n
个值,表示每个人的开心值,接下来是若干个关系,如1 3
表示第三号是第一号的直接boss,当输入0 0
的时候表示结束。
这里是保证关系是一个树,而不会有若干个树。
解题思路
显然这是个求解最优的题目,我们可以使用动态规划来进行解决,因为这个关系是一个树状的,所以也就称为树状dp
了。
这里我们先定义一个状态,dp[i][0]
表示在第i
号员工不参加的情况下,以他为根节点的树的最大值,同样的,dp[i][1]
就表示在第i
号员工参加的情况下,以他为根节点树的最大值。
对于初始化,dp[i][0]=0, dp[i][1]=他参加的开心值
。
递推关系,这里dp[i][1]={dp[son1][0]+dp[son2][0]+... 。son1,son2等表示i的直接孩子编号}
就是一个父节点参加,那么以他为根节点的树的最优值,就是以他的各个孩子节点(不参加时)为根节点树的最优值的和(这里满足最优子结构性质)。
dp[i][0] += max(dp[son][1], dp[son][0]),要对i的每个孩子节点进行判断
。
代码实现
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
typedef long long ll;
using namespace std;
const double esp=1e-6;
const int inf=0x3f3f3f3f;
const int MAXN=6e3+7;
int fa[MAXN], dp[MAXN][2];
vector<int> tre[MAXN];
bool vis[MAXN];
int n;
void dfs(int rt)
{
for(int i=0; i<tre[rt].size(); i++)
dfs(tre[rt][i]);
for(int i=0; i<tre[rt].size(); i++)
{
dp[rt][1] += dp[tre[rt][i]][0];
dp[rt][0] += max(dp[tre[rt][i]][0], dp[tre[rt][i]][1]);
}
}
int main()
{
while(cin>>n)
{
for(int i=1; i<=n; i++){
cin>>dp[i][1];
vis[i] = false;
fa[i] = i;
dp[i][0] = 0;
tre[i].clear();
}
int a, b;
while(cin>>a>>b){
if(a == 0 && b == 0)
break;
fa[a] = b;
tre[b].push_back(a);
}
int root;
for(int i=1; i<=n; i++)
if(fa[i] == i){
root = i;
break;
}
dfs(root);
cout<<max(dp[root][1], dp[root][0])<<endl;
}
return 0;
}