洛谷P1352 没有上司的舞会
题干简述如下:
某大学有
n
n
n 个职员,编号为
1
1
1 到
n
n
n,他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。每个人
i
i
i 都有一快乐指数
r
i
r_i
ri,宴会的快乐指数等于与会职员快乐指数之和。
但是,如果某个职员的直接上司来参加舞会,那么该职员就不可能参加舞会,点题“没有上司的舞会”。
输入:第一行
n
n
n,接下来
n
n
n 行表示
r
i
(
i
=
1
,
2
,
⋯
,
n
)
r_i(i=1,2,\cdots,n)
ri(i=1,2,⋯,n),之后输入
n
−
1
n-1
n−1 对整数对
(
l
,
k
)
(l,k)
(l,k) 表示
k
k
k 是
l
l
l 的直接上司。
输出:宴会的最大快乐指数。
前言
动态规划中,相当一部分是区间规划,但是此题是树形规划。对我来说,此题确实开阔了我的眼界,因为是我首次接触到树形规划。我也是在看这道题的题解时,意识到线性规划不必拘泥于数组,也可以拓展到树这种数据结构。树形规划的时间复杂度也没有我想象的那么大,总之这是一道与我而言非常有价值的题目。
解法
构造数组
d
p
[
i
]
[
j
]
(
j
=
0
,
1
)
dp[i][j](j=0,1)
dp[i][j](j=0,1),
j
=
0
j=0
j=0 表示当职员
i
i
i 不参加舞会时,职员
i
i
i 及其所有下属能够为舞会贡献的最大快乐指数;
j
=
1
j=1
j=1 表示当职员
i
i
i 参加舞会时,职员
i
i
i 及其所有下属能够为舞会贡献的最大快乐指数。
状态转移方程为:
{
d
p
[
x
]
[
0
]
=
∑
i
=
1
c
x
max
{
d
p
[
x
i
]
[
0
]
,
d
p
[
x
i
]
[
1
]
}
d
p
[
x
]
[
1
]
=
∑
i
=
1
c
x
d
p
[
x
i
]
[
0
]
+
r
x
(1)
\begin{cases}dp[x][0]=\displaystyle\sum_{i=1}^{c_x}\max\{dp[x_i][0],dp[x_i][1]\}\\ dp[x][1]=\displaystyle\sum_{i=1}^{c_x}dp[x_i][0]+r_x\end{cases}\tag{1}
⎩
⎨
⎧dp[x][0]=i=1∑cxmax{dp[xi][0],dp[xi][1]}dp[x][1]=i=1∑cxdp[xi][0]+rx(1)
其中 c x c_x cx 表示 x x x 的直接下属数量, x i x_i xi 表示 x x x 的第 i i i 个直接下属。最优子结构性比较明显,就不在此说明了。按照这个状态转移方程能够很容易地完成代码。
AC代码
# include <iostream>
# include <stdlib.h>
using namespace std;
struct {
int child,next;
} branch[6005];
struct {
int r,dad,fbranch,lbranch;
} node[6005];
int n,leader=1,dp[6005][2];
int max_(int a,int b){return a>b?a:b;}
int happy(int code,int par){
if(dp[code][par])
return dp[code][par];
if(par==0){
int ret=0,br=node[code].fbranch;
while(br){
ret+=max_(happy(branch[br].child,0),happy(branch[br].child,1));
br=branch[br].next;
}
return dp[code][par]=ret;
}
else{
int ret=0,br=node[code].fbranch;
while(br){
ret+=happy(branch[br].child,0);
br=branch[br].next;
}
return dp[code][par]=ret+node[code].r;
}
}
int main(){
cin>>n;
int l,k;
for(int i=1;i<=n;i++)
cin>>node[i].r;
for(int i=1;i<=n-1;i++){
scanf("%d%d",&l,&k);
node[l].dad=k;
if(node[k].fbranch==0){
node[k].fbranch=node[k].lbranch=i;
branch[i].child=l;
}
else{
branch[node[k].lbranch].next=i;
node[k].lbranch=i;
branch[i].child=l;
}
}
while(node[leader].dad) leader=node[leader].dad;
return printf("%d",max_(happy(leader,0),happy(leader,1))),0;
}
容易证明该算法的时间复杂度为 O ( n ) O(n) O(n),也算是比较快的算法了。