【题解】[JSOI2016] 最佳团体(分数规划)

P4322 JSOI2016 最佳团体 - 洛谷 (luogu.com.cn)

每个候选人 依赖 ,且 ,所以依赖关系形成一棵以 为根的树。

题目要求:如果选 ,就必须选 。这就是典型的树形依赖 dp,选子节点必须先选父节点。

但题目要最大化的是 不是直接最大化 ,所以不能直接背包,需要二分比值答案。


设计 dp 状态:dp[i][j] 表示从 dfs 序第 i 个位置开始,选 j 个节点的最大权值和。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 2510;

double S[N], P[N]; int R[N];
vector<int> G[N];
int dfn[N], tsp, id[N], siz[N];
int K, n;
double w[N], dp[N][N];
// dp[i][j] 表示从 dfs 序第 i 个位置开始,选 j 个节点的最大权值和

void dfs(int x) {
	dfn[x] = tsp;
	id[tsp] = x;
	tsp ++;
	siz[x] = 1;
	for (int y : G[x]) {
		dfs(y);
		siz[x] += siz[y];
	}
}

bool check(double x) {
	for (int i = 1; i <= n; i ++) {
		w[i] = P[i] - x * S[i];
	}
	w[0] = 0;
	for (int i = 0; i <= n + 1; i ++) {
		for (int j = 0; j <= K + 1; j ++) {
			dp[i][j] = -1e18;
		}
	}
	dp[n + 1][0] = 0;
	
	for (int i = n; i >= 0; i --) {   // 照 dfs 序从后往前遍历,也就是从叶子到根 
		int u = id[i];   // 当前 dfs 序对应的节点 
		for (int j = 0; j <= K + 1; j ++) {
			if (j >= 1) {
				dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + w[u]);
				// 当选了 u 节点,就可以从 u 的第一个孩子节点 i + 1 处转移 dp 
			}
			if (i + siz[u] <= n + 1) {
				dp[i][j] = max(dp[i][j], dp[i + siz[u]][j]);
				// 不选 u 节点,就只能从第一个不是 u 节点孩子转移,即跳过 u 的整个子树 
			}
			
			// 叶子节点没有孩子的特殊情况,上面两种分讨也包含了 
		}
	}
	/*
	假设某个叶子节点 v 被选中了
	在 dfs 序中,v 一定位于它所有祖先的子树区间内
	要从 dfs 序的起点走到 v,必须依次经过它的父节点、祖父节点......直到根

	在递推过程中,如果某个祖先 u 没有被选,那么根据情况一, 
	我们会直接跳过 u 的整个子树,根本不会进入 v 所在的区间
	因此 v 不可能被选中
	*/
	
	return (dp[0][K + 1] >= 0);   // 加上 0 就多选一个 
}

int main () {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin >> K >> n;
	for (int i = 1; i <= n; i ++) {
		cin >> S[i] >> P[i] >> R[i];
		G[R[i]].push_back(i);
	}
	
	tsp = 0;
	dfs(0);
	
	double l = 0, r = 10000;
	// 小数点后三位精确,也就是 10000000 的精确度
	// 2 ^ 50 完全够   
	for (int i = 0; i <= 50; i ++) {
		double mid = (l + r) / 2;
		if (check(mid)) {
			l = mid;
		}
		else {
			r = mid;
		}
	}
	
	cout << fixed << setprecision(3) << l << "\n";
	
	
	return 0;
}
相关推荐
晨曦中的暮雨13 分钟前
限流算法——固定窗口、滑动窗口、漏桶、令牌桶
java·开发语言·算法
怕浪猫21 分钟前
Claude Code vs Codex:Computer Use 实现原理深度拆解
算法·面试·github
罗西的思考2 小时前
AI 时代的 “房谋杜断”:如何培养能力、品味与判断力
人工智能·算法·机器学习
淡海水2 小时前
07-02-源码-.NET Core GC分配路径
算法·unity·c#·.netcore·gc·1024程序员节
choumin3 小时前
C++中万能引用与std::forward的作用和用法
c++·万能引用·std forward
下一站春风里4 小时前
位运算完整学习笔记:按位与(&)+ 异或(^)
开发语言·c++·算法
DRobot4 小时前
【机械制图与CAD实战(二十五)】螺纹的表示方法
算法·机械制图
用户老李5 小时前
卡尔曼滤波自学教程 10:无迹卡尔曼滤波(UKF)
算法
深圳市益普科技有限公司6 小时前
半导体良率管理YMS:良率与设备参数的相关性,先拉哪几组数据
人工智能·python·算法
努力努力再努力wz7 小时前
【C++高阶系列】C++20 协程底层原理:函数为什么能暂停?从状态机到 Task,彻底串起挂起、恢复与协程调度
开发语言·c++·c++20·1024程序员节