BZOJ 4753: [Jsoi2016]最佳团体

本文介绍了一种解决01分数规划问题的方法,通过二分查找和TreeDP技术求解最优解。具体步骤包括定义d[i] = p[i] - mid * s[i],并利用树形依赖背包进行子树大小合并,最终找到选取K个元素的最大值是否大于等于0。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这种题一看就知道是01分数规划啊233(你也不用去细究,就是二分。。

然后 令d[i]=p[i]-mid*s[i]
接着做个treeDP就好了啊 看看选K个的最大值是否>=0即可

因为按子树大小合并的背包是n^2 所以并不会有问题
当然也可以打树形依赖背包
DP这里就不说了 都很裸

你问我打了哪个? 我当然打简单一点的直接合并啊233

#include<bits/stdc++.h>
using namespace std;
const int N=2505;
const double eps=1e-5,inf=1e12;
char B[1<<14],*S=B,*T=B;
#define g (S==T&&(T=(S=B)+fread(B,1,1<<14,stdin),S==T)?-1:*S++)
inline int read(){
    int x=0,f=1; char ch=g;
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1; ch=g;}
    while(ch>='0' && ch<='9'){x=(x<<1)+(x<<3)+ch-'0'; ch=g;}
    return x*f;
}
struct edge{int y,nex;}a[N]; int len,fir[N];
void ins(int x,int y){
    a[++len]=(edge){y,fir[x]},fir[x]=len;
}
int s[N],p[N],m,n,sz[N]; double d[N],f[N][N],G[N];
void dfs(int x){
    sz[x]=1;
    f[x][1]=d[x];
    for(int k=fir[x];k;k=a[k].nex){
        int y=a[k].y; dfs(y);
        for(int i=1;i<=sz[x]+sz[y];++i)G[i]=f[x][i];
        for(int i=1;i<=sz[x];++i) if(f[x][i]>-inf)
            for(int j=1;j<=sz[y];++j) if(f[y][j]>-inf)
                G[i+j]=max(G[i+j],f[x][i]+f[y][j]);
        for(int i=1;i<=sz[x]+sz[y];++i)f[x][i]=G[i];
        sz[x]+=sz[y];
    }
}
int main(){
    m=read(),n=read(); int i,j;
    for(i=1;i<=n;++i){
        s[i]=read(),p[i]=read();
        int x=read(); ins(x,i);
    }
    double l=0,r=1e4,ans; d[0]=0;
    while(r-l>eps){
        double mid=(l+r)/2.0;
        for(i=0;i<=n;++i)for(j=1;j<=m+1;++j)f[i][j]=-inf;
        for(i=1;i<=n;++i)d[i]=(double)p[i]-mid*s[i];
        dfs(0);
        if(f[0][m+1]>eps)l=(ans=mid)+eps;
        else r=mid-eps;
    }
    printf("%.3lf\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值