题解 P4322 [JSOI2016]最佳团体
分数规划+树形背包。
可以根据推荐关系建出一颗树,然后如果选了一点,则该点到根上的所有点都必须选。
二分 ,定义每个结点的权值,然后判断选 个节点的最大值是否大于 。
设 为当前节点 ,在其子树内选了 个节点,最大点权和为多少。
转移方程为 。
初始化为 ,,其余皆为 。
然后我们发现这个 DP 的复杂度貌似是 。
但将各种限制条件加上后就变成了 。本人很菜,没法证明。
所以最终复杂度为 。
code:
#include <bits/stdc++.h>
using namespace std;
typedef double db;
const int N = 2500 + 5;
const db eps = 1e-5, inf = LLONG_MAX >> 1;
int k, n, fa[N], siz[N];
db s[N], p[N], val[N], f[N][N];
vector<int> adj[N];
void dfs(int u) {
siz[u] = 1; f[u][1] = val[u];
for (int i = 0; i < adj[u].size(); ++i) {
int v = adj[u][i]; if (v == fa[u]) continue;
dfs(v); siz[u] += siz[v];
for (int j = min(k + 1, siz[u]); j > 1; --j) {
for (int k = 0; k <= min(siz[v], j - 1); ++k) {
f[u][j] = max(f[u][j], f[u][j - k] + f[v][k]);
}
}
}
}
bool check(db mid) {
for (int i = 0; i <= n; ++i) val[i] = p[i] - mid * s[i];
for (int i = 0; i <= n; ++i) for (int j = 1; j <= k + 1; ++j) f[i][j] = -inf;
dfs(0);
return f[0][k + 1] > 0;
}
int main() {
cin >> k >> n;
for (int i = 1; i <= n; ++i) {
scanf("%lf%lf%d", &s[i], &p[i], &fa[i]);
adj[fa[i]].push_back(i);
}
double l = 0, r = 1000;
while (r - l > eps) {
db mid = (l + r) / 2;
if (check(mid)) l = mid;
else r = mid;
}
printf("%.3lf\n", l);
return 0;
}
本文作者:HQJ2007
本文链接:https://www.cnblogs.com/HQJ2007/p/17561300.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】