4753: [Jsoi2016]最佳团体
Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 1423 Solved: 517
[Submit][Status][Discuss]
Description
JSOI信息学代表队一共有N名候选人,这些候选人从1到N编号。方便起见,JYY的编号是0号。每个候选人都由一位
编号比他小的候选人Ri推荐。如果Ri=0则说明这个候选人是JYY自己看上的。为了保证团队的和谐,JYY需要保证,
如果招募了候选人i,那么候选人Ri"也一定需要在团队中。当然了,JYY自己总是在团队里的。每一个候选人都有
一个战斗值Pi",也有一个招募费用Si"。JYY希望招募K个候选人(JYY自己不算),组成一个性价比最高的团队。
也就是,这K个被JYY选择的候选人的总战斗值与总招募总费用的比值最大。
Input
输入一行包含两个正整数K和N。
接下来N行,其中第i行包含3个整数Si,Pi,Ri表示候选人i的招募费用,战斗值和推荐人编号。
对于100%的数据满足1≤K≤N≤2500,0<"Si,Pi"≤10^4,0≤Ri<i
Output
输出一行一个实数,表示最佳比值。答案保留三位小数。
Sample Input
1 2
1000 1 0
1 1000 1
Sample Output
0.001
HINT
2017.9.12新加数据一组 By GXZlegend
Source
题意: (中文题。。。)
思路: 要使得pi的和/ si的和 最大,我们就可以二分这个值,不断地逼近 最优解。然后用树形dp判断对于当前的mid 是否合理。这里的树形dp 其实是个依赖性背包问题。 推荐一个题。 看似是n3 其实是n2 的复杂度。 链接:
代码:
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-4;
const int N =2505;
struct node
{
double p;
double s;
double jj;
}a[N];
double dp[N][N];
int k,n;
vector<int >ve[N];
int sz[N];
/*
void dfs(int u){
sz[u]=1; dp[u][0]=0;
if(ve[u].size()==0){
dp[u][1]=a[u].jj;
return ;
}
//dp[u][1]=a[u].jj;
for(int i=0;i<ve[u].size();i++){
int v=ve[u][i];
dfs(v);
int mm=min(k+1,sz[u]);
//cout<<"minn "<<mm<<endl;
//cout<<"szv "<<sz[v]<<endl;
for(int j=mm;j>=0;j--){
for(int kk=0;kk<=sz[v];kk++){
if(j+kk>k+1) break;
dp[u][j+kk]=max(dp[u][j+kk],dp[u][j]+dp[v][kk]);
}
}
sz[u]+=sz[v];
}
for(int i=k+1;i>=0;i--){
if(i>=1) dp[u][i]=dp[u][i-1]+a[u].jj;
else dp[u][i]=0;
}
}
*/
void dfs(int u)
{
sz[u]=1;
if(ve[u].size()==0){
dp[u][0]=0; dp[u][1]=a[u].jj;
return ;
}
dp[u][1]=a[u].jj;
// 这里并没有将dp[u][0] 设为0 而是 -INF 正是体现了依赖性关系。因为如果我的父节点不选的话,子节点是不可能选的。
for(int i=0;i<ve[u].size();i++)
{
int v=ve[u][i];
dfs(v);
int mm=min(sz[u],k+1);
for(int j=mm;j>=0;j--)
{
for(int kk=0;kk<=sz[v];kk++)
{
if(j+kk>k+1) break;
dp[u][j+kk]=max(dp[u][j+kk],dp[u][j]+dp[v][kk]);
}
}
sz[u]+=sz[v];
}
}
int jud(double hh)
{
for(int i=0;i<=n;i++){
a[i].jj=a[i].p-a[i].s*hh;
}
memset(sz,0,sizeof(sz));
memset(dp,0xc2,sizeof(dp));
dfs(0);
/*
cout<<"****"<<endl;
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
cout<<dp[i][j]<<" ";
}
cout<<endl;
}
cout<<"**** "<<endl;
*/
if(dp[0][k+1]>-eps) return 1;
return 0;
}
int main()
{
int u;
scanf("%d %d",&k,&n);
double l=0,r=-1;
for(int i=1;i<=n;i++)
{
scanf("%lf %lf %d",&a[i].s,&a[i].p,&u);
r=max(r,a[i].p);
ve[u].push_back(i);
}
a[0].s=a[0].p=0;
//jud(0.001);
double mid;
double ans=l;
while(r-l>eps)
{
mid=(l+r)/2;
//cout<<"mid "<<mid<<endl;
if(jud(mid)){
ans=mid;
l=mid;
}
else r=mid;
}
printf("%.3f\n",ans);
return 0;
}