最佳团体(jsoi2016)——01分数规划&&树形dp

题目描述
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

题解
根据公式 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;
} 

猜你喜欢

转载自blog.csdn.net/chm_wt/article/details/81839059