(树形dp)Spring tree
题目链接
题目概述
给定n个铁球,重量为wi,再给定n - 1条弹簧(可变的边权)所链接的两端,每个位置上的铁球可以相互交换。弹簧的长度为每个节点的子树边权和+1。问从根节点(1节点)开始的最大深度。
输入 #1
4
1 2 3 4
1 2
2 3
3 4
输出 #1
23
样例说明
In the test case, keep original arrangement is a good idea.
maximum depth = (1+4) + (1+4+3) + (1+4+3+2) = 23
思路
首先我们发现,更重的球会更倾向于放到深度最深的位置。
枚举最优解中深度最深的叶子 leaf,那么最优解会怎样放置呢?
首先最重的球一定给 leaf,然后考虑 leaf 的父亲 parent(leaf),那么最重的 size(parent(leaf)) 个球一定都在 parent(leaf) 的子树中(其中 size(u) 表示 u 的子树大小)..... 依次类推。
对球的重量排序,用 w(k) 表示 k 个最重的球的总重量。
那么我们可以把节点 u 的权值设成 w(size(u)) 接着求出树上从根到树叶找出一条权值和最大的路径即可,这个可以用 O(n) 的 DP 来实现,需要注意的是根节点没有连向父亲的弹簧,所以根节点权值应该设成 0.
AcCode:
#include <bits/stdc++.h>
using namespace std;
#define sp ' '
#define endl '\n'
#define push_back pb
typedef long long ll;
typedef vector<int> vi;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 10;
int n, m, cnt, ans;
int tree[N], siz[N], sum[N];
vi g[N];
vi w;
void dfs1(int u, int fa){
siz[u] = 1;
for(auto v : g[u]){
if(v != fa) dfs1(v, u);
siz[u] += siz[v];
}
}
void dfs2(int u, int fa){
int res = 0;
for(auto v : g[u]){
if(v != fa){
dfs2(v, u);
res = max(res, tree[v] + sum[siz[v]] + 1);
}
}
tree[u] = res;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
rep(i, 1, n){
int a; cin >> a;
w.pb(a);
}
sort(all(w), greater<int>());
rep(i, 1, n) sum[i] = sum[i - 1] + g[i];
rep(i, 1, n - 1){
int u, v; cin >> u >> v;
g[u].pb(v); g[v].pb(u);
}
dfs1(1, -1);
dfs2(1, -1);
cout << tree[1] << endl;
return 0;
}

浙公网安备 33010602011771号