P8564 ρars/ey 题解
显然树上背包。
首先一眼会想到的状态:dpi,j 表示 i 的子树最后剩下 j 个结点的最小代价。
然而开始写会发现这并不好 DP。
于是我们换一个想法:dpi,j 表示 i 的子树删去 j 个结点的最小代价。
则有转移方程:
dpi,j=min
但是注意到这个方程 j 最多到 siz_{i} - 1 - cntson_{i}(cntson_{i} 表示 i 的儿子的数量),我们还需要一个方程:
dp_{i,siz_{i} - 1} = \min\{dp_{i,j} + f_{siz_{i} - j}\}
于是这样就完美了,O(n^2) 足够通过此题
代码如下:
#include<bits/stdc++.h>
#define MAXN 5010
#define INF 0x7f7f7f7f7f7f7f7f
using namespace std;
typedef long long ll;
struct edge{ int pre, to; };
edge e[MAXN << 1];
int n, cnt;
int head[MAXN], f[MAXN];
namespace strange{
ll dp[MAXN][MAXN], siz[MAXN];
void dfs(int now, int fa){
dp[now][0] = 0; siz[now] = 1;
for(int i = head[now]; i; i = e[i].pre){
if(e[i].to == fa) continue;
dfs(e[i].to, now);
for(int j = siz[now] + siz[e[i].to] - 1; j > 0; j--){
for(int k = max(j - siz[now], 1ll); k <= siz[e[i].to] - 1 && k <= j; k++){
if(dp[e[i].to][k] >= INF || dp[now][j - k] >= INF) continue;
dp[now][j] = min(dp[now][j], dp[e[i].to][k] + dp[now][j - k]);
}
}
siz[now] += siz[e[i].to];//记得先 DP 后加 siz,否则复杂度是假的,这个蒟蒻就是因为这个少了 30 分
}
for(int j = 0; j < siz[now] - 1; j++){
dp[now][siz[now] - 1] = min(dp[now][siz[now] - 1], dp[now][j] + f[siz[now] - j]);
}
}
void main(){
memset(dp, 0x7f, sizeof(dp));
dfs(1, 0);
printf("%lld\n",dp[1][siz[1] - 1]);
}
}
void add_edge(int u, int v){
e[++cnt].pre = head[u];
e[cnt].to = v;
head[u] = cnt;
}
int main(){
// freopen("T2ex2.in", "r", stdin);
scanf("%d",&n);
for(int i = 1; i < n; i++) scanf("%d",&f[i + 1]);
for(int i = 1; i < n; i++){
int u, v; scanf("%d%d",&u,&v);
add_edge(u, v); add_edge(v, u);
}
strange::main();
return 0;
}
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步