题目描述
JSOI 信息学代表队一共有 N 名候选人,这些候选人从 1 到 N 编号。方便起见,JYY 的编号是 0 号。每个候选人都由一位编号比他小的候选人 Ri 推荐。如果 Ri=0 则说明这个候选人是 JYY 自己看上的。为了保证团队的和谐,JYY 需要保证,如果招募了候选人 i,那么候选人 Ri” 也一定需要在团队中。当然了,JYY 自己总是在团队里的。每一个候选人都有一个战斗值 Pi”,也有一个招募费用 Si”。JYY 希望招募 K 个候选人(JYY 自己不算),组成一个性价比最高的团队。
也就是,这 K 个被 JYY 选择的候选人的总战斗值与总招募总费用的比值最大。
输入
输入一行包含两个正整数 K 和 N。
接下来 N 行,其中第 i 行包含 3 个整数 Si,Pi,Ri 表示候选人 i 的招募费用,战斗值和推荐人编号。
对于 100% 的数据满足 1≤K≤N≤2500,0<”Si,Pi”≤10^4,0≤Ri<=i
输出
输出一行一个实数,表示最佳比值。答案保留三位小数。
样例输入
1 2
1000 1 0
1 1000 1
样例输出
0.001
题解
根据公式
∑ni=1p[i]∑ni=1s[i]
∑
i
=
1
n
p
[
i
]
∑
i
=
1
n
s
[
i
]
很容易想到用分数规划来解决问题
二分 ans,树形 dp 判断是否符合即可
代码
#include<bits/stdc++.h>
#define eps 1e-4
using namespace std;
struct node{int to,nex;}e[5002];
int n,m,p[2502],s[2502],head[2502],cnt,siz[2502];
double d[2502],f[2502][2502],l,r;
void add(int s,int t){e[++cnt].to=t;e[cnt].nex=head[s];head[s]=cnt;}
void dfs(int x){
if(x) f[x][1]=d[x];
else f[x][0]=0;
siz[x]=x?1:0;
for(int k=head[x];k;k=e[k].nex){
dfs(e[k].to);
int len=x?1:0;
for(int j=min(m,siz[x]);j>=len;j--)
for(int l=1;l<=min(m-j,siz[e[k].to]);l++)
f[x][j+l]=max(f[x][j+l],f[x][j]+f[e[k].to][l]);
siz[x]+=siz[e[k].to];}
}
bool pd(double mid){
for(int i=1;i<=n;i++) d[i]=(double)p[i]-mid*s[i];
for(int i=0;i<=n;i++) for(int j=0;j<=m;j++) f[i][j]=-1e9;
dfs(0);
return f[0][m]>0;
}
int main(){
cin>>m>>n;
for(int i=1,t;i<=n;i++){
scanf("%d%d%d",&s[i],&p[i],&t);
r=max(r,(double)p[i]);
add(t,i);}
while(r-l>eps){
double mid=(l+r)/2;
if(pd(mid)) l=mid;
else r=mid;}
printf("%.3lf\n",l);
return 0;
}