题目描述
洛谷传送门
题目描述
JSOI 信息学代表队一共有 N 名候选人,这些候选人从 1 到 N 编号。方便起见,JYY 的编号是 0 号。每个候选人都由一位编号比他小的候选人Ri推荐。如果 Ri=0,则说明这个候选人是 JYY 自己看上的。
为了保证团队的和谐,JYY 需要保证,如果招募了候选人 iii,那么候选人 Ri 也一定需要在团队中。当然了,JYY 自己总是在团队里的。每一个候选人都有一个战斗值 Pi,也有一个招募费用 Si 。JYY 希望招募 K 个候选人(JYY 自己不算),组成一个性价比最高的团队。也就是,这 K 个被 JYY 选择的候选人的总战斗值与总招募费用的比值最大。
解析
二分这个比值
再加上一些推导就可以转化为树上的nm背包问题
(nm背包传送门)
这样就完事了
#include <bits/stdc++.h>
using namespace std;
const int N=3000;
int n,m;
struct node{int to,nxt;
}p[N];
int fi[N],cnt=-1,ru[N];
int pp[N],s[N],belong[N];
double P[N];
void addline(int x,int y){p[++cnt]=(node){y,fi[x]};fi[x]=cnt;
}
int a,b,c;
int pos[N],dfs[N],size[N],tot;
void build(int x,int fa){size[x]=1;for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;build(to,x);size[x]+=size[to];}dfs[x]=++tot;pos[tot]=x;
}
double dp[N][N];
bool check(double mid){
// printf("\ncheck:%lf\n",mid);for(int i=1;i<=n;i++) P[i]=pp[i]-mid*s[i];for(int i=0;i<=tot;i++){for(int j=1;j<=m+1;j++) dp[i][j]=-2e9;}dp[0][0]=0;for(int i=1;i<=tot;i++){int id=pos[i];for(int j=m+1;j>=1;j--){dp[i][j]=max(dp[i-1][j-1]+P[id],dp[i-size[id]][j]);
// printf("i=%d j=%d dp=%lf size=%d\n",i,j,dp[i][j],size[id]);}}
// printf("%lf\n",dp[tot][m+1]);return dp[tot][m+1]>=0;}
int main(){memset(fi,-1,sizeof(fi));scanf("%d%d",&m,&n);for(int i=1;i<=n;i++){scanf("%d%d%d",&s[i],&pp[i],&belong[i]);addline(belong[i],i);}build(0,-1);double st=0,ed=1e5+100;while(ed-st>=0.00001){double mid=(st+ed)/2;
// printf("st=%d ed=%d\n",st,ed);if(check(mid)) st=mid;else ed=mid;}printf("%.3lf",st);return 0;
}
/*
1 2
100 1000 0
1 1000 1
*/