期末考前写题解,(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<cstdio>
#include<algorithm>
#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<n;i++)
{
int u=read(),fa=read();
sig[u]=true,add(fa,u);
}
read(),read();
int root;
for(RI i=1;i<=n;i++)
if(sig[i]==false)
{
root=i;
break;
}
dp(root);
printf("%d
",max(f[root][0],f[root][1]));
return 0;
}
[thanks for watching
]