tree dp;
考虑祖先对孩子的影响:
有g(i,j)表示 i 这个节点获得先祖的 j 深度的赏赐覆盖全部子树的最小代价
考虑孩子对祖先兄弟等的影响:
有f(i,j)表示 i 这个节点覆盖了全部子树且向外延伸j的最小代价
转移显然;
#include<cstdio>
#include<algorithm>
#include<cstring>
#define rep(i,k,n) for(int i=k;i<=n;i++)
using namespace std;
const int N=1e5+7;
const int M=105;
const int inf=0x3f3f3f3f;
int f[N][M],g[N][M],w[N],n,W=100;
struct E{
int to,next;E(int to=0,int next=0):to(to),next(next){}
}edge[N<<1];
int head[N],tot=0;
void add(int x,int y){
edge[++tot]=E(y,head[x]);head[x]=tot;
edge[++tot]=E(x,head[y]);head[y]=tot;
}
void dp(int x,int fa){
for(int i=head[x];i;i=edge[i].next){
int v=edge[i].to;
if(v!=fa){
dp(v,x);
rep(j,1,W+1){
g[x][j]+=g[v][j-1];
}
}
}
rep(i,0,w[x])f[x][i]=min(f[x][i],g[x][i+1]+1);
for(int i=head[x];i;i=edge[i].next){
int v=edge[i].to;
if(v!=fa){
rep(j,0,W)f[x][j]=min(f[x][j],g[x][j+1]-g[v][j]+f[v][j+1]);
}
}
g[x][0]=inf;
rep(i,0,W)g[x][0]=min(g[x][0],f[x][i]);
rep(i,1,W)g[x][i]=min(g[x][i-1],g[x][i]);
}
int main(){
while(~scanf("%d",&n)){
memset(f,0x3f,sizeof(f));
memset(g,0,sizeof(g));
memset(head,0,sizeof(head));
tot=0;
rep(i,1,n)scanf("%d",&w[i]);
rep(i,1,n-1){int x,y;scanf("%d%d",&x,&y);add(x,y);}
dp(1,0);
printf("%d\n",g[1][0]);
}
}
启示:
1。非常有必要在自己完全忘记某道题做法时独立推导一遍;
2。特别是dp,下意识考虑所有情况,你的解法要应对所有;
3。时刻反问自己算法的正确性,状态的表示;