[题解] P8564 ρars/ey
\color{red}博客内食用效果更佳(点我)
思路:树上背包
复杂度:O(n^2)
题解中涉及到的变量:
| 变量名 | 含义 |
|---|---|
| 操作代价(题目给出的) | |
| 对于节点 |
主体思路
操作代价与当前子树大小相关,不难想到用树上背包去解决此问题,设
当不对当前节点进行删除操作时:
转移方程:
注意到这样转移复杂度不够优,考虑控制上下界,设
当对当前节点进行删除操作时:
转移方程(其中
代码实现需要注意的地方:
- 一定要注意上下界的限制,不然退化为
O(n^3) 。 - 开 long long 防炸。
- 注意初值设置。
参考代码:
代码中的
参考代码中其他变量名与题解中均一致,详见注释。
#define LL long long
#define UN unsigned
#include<bits/stdc++.h>
using namespace std;
//--------------------//
const int N=5005;
int n,val[N];
//--------------------//
//Edge
const int M=1e4+5;
struct Edge
{
int to,nex;
}edge[M];
int tot,head[N];
void add(int from,int to)
{
edge[++tot]={to,head[from]};
head[from]=tot;
}
//----------//
//DP
LL f[N][N];
int siz[N];
void DFS(int now,int fa)
{
siz[now]=1;
f[now][0]=0;
for(int to,cnt=0,i=head[now];i;i=edge[i].nex)
{
to=edge[i].to;
if(to==fa)
continue;
DFS(to,now);
cnt++;//更新子节点个数
for(int j=siz[now]+siz[to]-1-cnt;j>0;j--)//倒序枚举,上下界优化
for(int k=max(j-siz[now],1);k<=min(siz[to]-1,j);k++)
f[now][j]=min(f[now][j],f[now][j-k]+f[to][k]);//转移
siz[now]+=siz[to];//更新子树大小
}
for(int i=0;i<siz[now]-1;i++)//对当前节点进行操作的最小代价
f[now][siz[now]-1]=min(f[now][siz[now]-1],f[now][i]+val[siz[now]-1-i]);
}
//--------------------//
int main()
{
memset(f,0x3f,sizeof(f));
scanf("%d",&n);
for(int i=1;i<n;i++)
scanf("%d",&val[i]);
for(int from,to,i=1;i<n;i++)
scanf("%d%d",&from,&to),add(from,to),add(to,from);
DFS(1,0);
printf("%d",f[1][siz[1]-1]);//答案,显著的
return 0;
}