• 思路:二分答案+动态规划(结合dfs序)
  • 类型:选/不选:最大比值
  • 代码:
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int N=2505;
int rt=0,k,n,f[N];
double eps=1e-6,v[N],s[N],p[N],dp[N][N],sum;
int tot,head[N],nxt[N*2],to[N*2],size[N],Time,od[N];
void add_edge(int u,int v) {
	tot++; nxt[tot]=head[u]; to[tot]=v; head[u]=tot;
}
void dfs(int u) {
	size[u]=1;
	od[Time++]=u;		//某时间戳对应的点u子树[od[time],od[time+size[u]]]
	for(int i=head[u];i;i=nxt[i]) {
		int v=to[i];
		dfs(v);
		size[u]+=size[v];
	}
}
bool check(double ans) {
	for(int i=1;i<=n;i++) v[i]=p[i]-s[i]*ans;
	for(int i=1;i<=n+1;i++) for(int j=0;j<=k;j++) dp[i][j]=-sum;
	dp[1][0]=0.0;
	for(int i=1;i<=n;i++) {		//time
		for(int j=0;j<=min(i,k);j++) {
			dp[i+1][j+1]=max(dp[i+1][j+1],dp[i][j]+v[od[i]]);	//选这个点
			dp[i+size[od[i]]][j]=max(dp[i+size[od[i]]][j],dp[i][j]);	//不选,直接跳过该子树 
		}
	}
	if(dp[1+n][k]>=0) return true;
	return false;
}
double solve(double l,double r) {
	double ans,mid;
	while(r-l>=eps) {
		mid=(l+r)/2;
		if(check(mid)) {
			ans=mid; l=mid;
		}
		else r=mid;
	}
	return ans;
}
int main() {
	scanf("%d%d",&k,&n);
	for(int i=1;i<=n;i++) {
		scanf("%lf%lf%d",&s[i],&p[i],&f[i]);
		sum+=p[i];
		add_edge(f[i],i);
	}
	dfs(0);
	double ans=solve(0,sum);
	printf("%.3lf",ans);
	return 0;
}