BZOJ 4753(树形dp+分数规划)

本文介绍了一道JSOI2016竞赛题目的解决方案,该题目要求在考虑成员间推荐关系的前提下,从N名候选人中选出K名以组建战斗力与招募费用比值最大的团队。文章详细解释了通过二分查找与树形DP相结合的方法来求解此问题,并附带了完整的C++代码实现。
摘要由CSDN通过智能技术生成

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

 

[Submit][Status][Discuss]

题意: (中文题。。。)

思路: 要使得pi的和/ si的和 最大,我们就可以二分这个值,不断地逼近 最优解。然后用树形dp判断对于当前的mid 是否合理。这里的树形dp 其实是个依赖性背包问题。 推荐一个题。 看似是n3 其实是n2 的复杂度。 链接:

CF815c

代码:

#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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值