期末考前写题解,\(rp++\)
\[ description \]
给出一棵点带权树,你需要选取若干个点:
\(<1> :\) 选取的点两两不具有父子关系。
\(<2> :\) 在满足 \(<1>\) 的情况下最大化点权和。
求最大的点权和。
\[ solution \]
树形 \(dp\) 经典。
令 f[u][0]
表示在不选 \(u\) 的情况下,在以 \(u\) 为根的子树中取点的最大点权和。
令 f[u][1]
表示在选取 \(u\) 的情况下,在以 \(u\) 为根的子树中取点的最大点权和。
若不选 \(u\) ,则对于 \(u\) 的任意一个儿子,取不取都不会与 \(u\) 形成父子关系,则有状态转移方程:
\[ f[u][0]=\sum_{v=\operatorname{son}(u)}\operatorname{max}(f[v][0],f[v][1]) \]
若选取 \(u\) ,则不能选取 \(u\) 的任意一个儿子,则有状态转移方程:
\[ f[u][1]=val[u]+\sum_{v=\operatorname{son}(u)}f[v][0] \]
答案为 \(\operatorname{max}(f[root][0],f[root][1])\) 。
一次树形 \(dp\) 即可解决。
\[ code \]
#include
#include
#define RI register int
using namespace std;
inline int read()
{
int x=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-f;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
return x*f;
}
const int N=6100,M=6100;
int n;
int val[N];
bool sig[N];
int tot,head[N],ver[M],Next[M];
void add(int u,int v)
{
ver[++tot]=v; Next[tot]=head[u]; head[u]=tot;
}
int f[N][2];
void dp(int u)
{
f[u][0]=0;
f[u][1]=val[u];
for(RI i=head[u];i;i=Next[i])
{
int v=ver[i];
dp(v);
f[u][0]+=max(f[v][0],f[v][1]);
f[u][1]+=f[v][0];
}
}
int main()
{
n=read();
for(RI i=1;i<=n;i++)
val[i]=read();
for(RI i=1;i
\[ thanks \ for \ watching \]