链接
https://www.luogu.org/problemnew/show/P3262
题解
这题不简单
可以考虑最终的答案等于
∑
x
∈
l
e
a
v
e
s
c
o
n
t
r
i
b
u
t
i
o
n
(
x
)
\sum_{x\in leaves}contribution(x)
x∈leaves∑contribution(x)
其中
c
o
n
t
r
i
b
u
t
i
o
n
(
x
)
contribution(x)
contribution(x)是这个结点对于答案的贡献,也就是对于从
x
x
x到根节点这条链上的贡献
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示以
i
i
i为根的子树中的叶子对答案的最终贡献(也就是说把
i
i
i的祖先也考虑进去了)
但是要确定贡献,就要确定这条链上每个点的属性,否则这个
d
p
dp
dp的后效性就无法消除
那我就暴力枚举每个点的颜色,这样我在做一个点的树形
d
p
dp
dp的时候就只需要进行普通的背包合并了
复杂度可以这么算
T
(
n
)
=
4
T
(
n
/
2
)
+
2
n
−
1
T(n)=4T(n/2)+2^{n-1}
T(n)=4T(n/2)+2n−1,
T
(
1
)
=
2
T(1)=2
T(1)=2
得到
T
(
n
)
∈
O
(
2
2
n
)
T(n)\in O(2^{2n})
T(n)∈O(22n)
总结
d
p
dp
dp后效性的消除是个比较难的点,我见过的主要有以下三种方式:
1.这题也许有某种特别的性质,通过某种状态设计或者对数据的预处理可以出现无后效性的问题
2.干脆加维度,分开计算
3.像这道题一样,干脆爆搜出所有的情况,取
m
a
x
max
max
代码
#include <bits/stdc++.h>
#define maxn 2019
using namespace std;
int f[maxn][maxn], N, n, war[maxn][20], farm[maxn][20], cat[maxn], M;
void dfs(int pos, int d)
{
for(auto i=0;i<=(1<<d);i++)f[pos][i]=0;
if(d==0)
{
for(auto i=1;i<N;i++)
{
if(cat[i])f[pos][1]+=war[pos][i];
else f[pos][0]+=farm[pos][i];
}
return;
}
cat[d]=0;
dfs(pos<<1,d-1);
dfs(pos<<1|1,d-1);
for(auto i=0;i<=(1<<d);i++)
for(auto j=0;j<=i;j++)
{
f[pos][i]=max(f[pos][i],f[pos<<1][j]+f[pos<<1|1][i-j]);
}
cat[d]=1;
dfs(pos<<1,d-1);
dfs(pos<<1|1,d-1);
for(auto i=0;i<=(1<<d);i++)
for(auto j=0;j<=i;j++)
{
f[pos][i]=max(f[pos][i],f[pos<<1][j]+f[pos<<1|1][i-j]);
}
}
int main()
{
ios::sync_with_stdio(false);
cin>>N>>M;
for(auto i(1<<N-1);i<(1<<N);i++)for(auto j(1);j<N;j++)cin>>war[i][j];
for(auto i(1<<N-1);i<(1<<N);i++)for(auto j(1);j<N;j++)cin>>farm[i][j];
dfs(1,N-1);
int ans(0);
for(auto j(0);j<=M;j++)ans=max(ans,f[1][j]);
cout<<ans;
return 0;
}