题目链接
题意
一个大小为
n
n
n 的树,每点权值
<
s
i
,
p
i
>
<si,pi>
<si,pi>,恰好K个点,要求
∑
p
∑
s
\frac{\sum{p}}{\sum{s}}
∑s∑p最大。
还要满足
f
[
u
]
f[u]
f[u] 选了才能选
u
u
u
思路
01分数规划,二分枚举答案,
∑
p
∑
s
>
=
a
n
s
\frac{\sum{p}}{\sum{s}} >= ans
∑s∑p>=ans
等
价
等价
等价
∑
p
−
a
n
s
∗
∑
s
>
=
0
\sum{p}-ans*\sum{s} >= 0
∑p−ans∗∑s>=0
然后用树形背包解决,范围较大需要优化。
代码
递归上下界优化
#include <bits/stdc++.h>
using namespace std;
int m, n;
int first[2505], tot, sz[2505];
double dp[2505][2505], a[2505];
struct Node {
int a, b, nxt, v;
}e[2505];
void add(int u, int v, int a, int b)
{
e[tot].a = a;
e[tot].b = b;
e[tot].v = v;
e[tot].nxt = first[u];
first[u] = tot++;
}
void dfs(int u, double x) {
sz[u]=1;
for (int i = first[u]; ~i; i = e[i].nxt) {
int v = e[i].v;
dp[v][1] = e[i].b-x*e[i].a;
dfs(v, x);
for(int j = min(m+1, sz[u]+sz[v]); j >= 1; --j) {
for(int k = max(1, j-sz[u]); k <= sz[v] && k < j; ++k) {
dp[u][j] = max(dp[u][j], dp[u][j-k] + dp[v][k]);
}
}
sz[u] += sz[v];
}
}
int solve(double x) {
for(int i = 0; i <= n; ++i) for(int j = m+1; ~j; --j) dp[i][j] = -1e9;
dp[0][1] = 0;
dfs(0,x);
return dp[0][m+1] >= 0;
}
int main() {
while(~scanf("%d%d",&m,&n)) {
memset(first,-1,sizeof(first));
tot = 0;
for(int i = 1; i <= n; ++i) {
int a, b, c;
scanf("%d%d%d",&a,&b,&c);
add(c,i,a,b);
}
double l = 0.0, r = 10000, mid;
while(r-l > 1e-5) {
mid = (l+r)/2;
if(solve(mid)) l = mid;
else r = mid;
}
printf("%.3f\n",l);
}
return 0;
}
先序遍历优化
#include <bits/stdc++.h>
using namespace std;
int m, n;
vector<int> e[2505];
int sz[2505], tot, id[2505];
double dp[2505][2505], a[2505], b[2505];
void dfs(int u) {
sz[u] = 1;
id[++tot] = u;
for(auto v : e[u]) dfs(v), sz[u] += sz[v];
}
int solve(double mid) {
for(int i = 0; i <= tot+1; ++i) for(int j = 0; j <= m; ++j) dp[i][j] = -1e9;
for(int i = 0; i <= tot+1; ++i) dp[i][0] = 0;
for(int i = tot; i; --i) {
int x = id[i];
for(int j = 1; j <= m; ++j) {
dp[i][j] = max(dp[i+1][j-1]+b[x]-mid*a[x], dp[i+sz[x]][j]);
}
}
return dp[1][m] >= 0;
}
int main() {
while(~scanf("%d%d",&m,&n)) {
++m;
for(int i = 0; i <= n; ++i) e[i].clear();
for(int i = 1; i <= n; ++i) {
int c;
scanf("%lf%lf%d",&a[i],&b[i],&c);
e[c].push_back(i);
}
tot = 0;
dfs(0);
double l = 0.0, r = 10000, mid;
while(r-l > 1e-5) {
mid = (l+r)/2;
if(solve(mid)) l = mid;
else r = mid;
}
printf("%.3f\n",l);
}
return 0;
}

本文探讨了一种结合树形背包与01分数规划的算法,用于求解特定树结构中节点选择问题,以实现权值比的最大化。通过递归上下界优化和先序遍历优化两种方式,详细阐述了算法的实现过程,并提供了完整的代码示例。

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



