题解 P4322 [JSOI2016]最佳团体

P4322 [JSOI2016]最佳团体

分数规划+树形背包。

可以根据推荐关系建出一颗树,然后如果选了一点,则该点到根上的所有点都必须选。

二分 mid,定义每个结点的权值,然后判断选 k+1 个节点的最大值是否大于 0

fi,j 为当前节点 i,在其子树内选了 j 个节点,最大点权和为多少。

转移方程为 fu,j=max(fu,j,fu,jt+fv,t)

初始化为 fu,0=0fu,1=valu,其余皆为 inf

然后我们发现这个 DP 的复杂度貌似是 O(n3)

但将各种限制条件加上后就变成了 O(n2)。本人很菜,没法证明。

所以最终复杂度为 O(n2logn)

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 中国大陆许可协议进行许可。

posted @   HQJ2007  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起