题意
给出一棵树,求一条路径,选择路上的V个点,使得被选择的点的相邻且不在路径上的点的权值和最大。
思路
定义:
c[i][j]c[i][j]为从ii点的子树中走到i,选择j个点的权值和。
为从ii点开始,向子树中走,选择j个点的权值和。
为ii的相邻节点的权值和。
为ii的父节点。
为ii的权值。
初始值:
b[x][0]=0,b[x][i]=g[x]−F[fa[x]]b[x][0]=0,b[x][i]=g[x]−F[fa[x]]
转移方程:
c[x][i]=max(c[y][i],c[y][i−1]+g[x]−F[y])c[x][i]=max(c[y][i],c[y][i−1]+g[x]−F[y])
b[x][i]=max(b[y][i],b[y][i−1]+g[x]−F[fa[x]])b[x][i]=max(b[y][i],b[y][i−1]+g[x]−F[fa[x]])
代码
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
const int maxn=1e5+5;
int N,V,x,y,tot,top,F[maxn],sta[maxn];
int son[maxn<<1],nxt[maxn<<1],lnk[maxn];
long long g[maxn],c[maxn][105],b[maxn][105],ans;
inline int read() {
int ret=0,f=1,ch=getchar();
for (; !isdigit(ch); ch=getchar()) if (ch=='-') f=-f;
for (; isdigit(ch); ch=getchar()) ret=ret*10+ch-48;
return ret*f;
}
inline void add_edge(int x,int y) {
son[++tot]=y,nxt[tot]=lnk[x],lnk[x]=tot;
son[++tot]=x,nxt[tot]=lnk[y],lnk[y]=tot;
}
inline void DP(int x,int y,int f) {
// ans一定要在前面赋值,这样可以避免从y走到x又走回y
for (int i=1; i<=V; ++i) ans=max(ans,c[x][i]+b[y][V-i]);
for (int i=1; i<=V; ++i)
c[x][i]=max(c[x][i],max(c[y][i],c[y][i-1]+g[x]-F[y])),
b[x][i]=max(b[x][i],max(b[y][i],b[y][i-1]+g[x]-F[f]));
}
void dfs(int x,int pre) {
for (int i=1; i<=V; ++i) c[x][i]=g[x],b[x][i]=g[x]-F[pre];
for (int k=lnk[x]; k; k=nxt[k]) if (son[k]^pre) dfs(son[k],x),DP(x,son[k],pre);
// 对于节点x,y和z是它的子节点并且,y先于z遍历。
// 上面的方法,无法计算z->x->y所以要倒着做一遍
for (int i=1; i<=V; ++i) c[x][i]=g[x],b[x][i]=g[x]-F[pre];
top=0;for (int k=lnk[x]; k; k=nxt[k]) if (son[k]^pre) sta[++top]=son[k];
for (int i=top; i; --i) DP(x,sta[i],pre);
ans=max(ans,max(c[x][V],b[x][V]));
}
int main() {
N=read(),V=read();
for (int i=1; i<=N; ++i) F[i]=read();
for (int i=1; i<N; ++i) add_edge(x=read(),y=read()),g[x]+=F[y],g[y]+=F[x];
return dfs(1,0),printf("%lld",ans),0;
}
本文介绍了一种使用树形动态规划方法解决特定路径选择问题的算法。该问题要求在一棵树上找出一条路径并选择V个点,使这些点的相邻且不在路径上的点的权值和达到最大。文章详细阐述了状态定义、初始值设定及状态转移方程,并提供了完整的C++代码实现。
546

被折叠的 条评论
为什么被折叠?



