P4322 [JSOI2016]最佳团体 题解

两个知识点

第一个是01分数规划

第二个是树形背包,树形背包请看另一篇博客

01分数规划主要用于求如柿子 $\huge\frac{\sum a_i}{\sum b_i}$的最大值

那么设这个柿子=k,则有 $\sum a_i-k\sum b_i≥0$ 如果说是一个序列,那么我们可以二分k值,直接对所有元素用$a-kb$的值进行排序,选择其中最大的x个判断其是否大于0

但是这个题不是一个序列,还有树关系。 其实也很简单,套上树形背包即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const double eps=1e-7;
inline int r()
{
    int s=0,k=1;char c=getchar();
    while(!isdigit(c))
    {
        if(c=='-')k=-1;
        c=getchar();
    }
    while(isdigit(c))
    {
        s=s*10+c-'0';
        c=getchar();
    }
    return s*k;
}
int n,k,pre[10001],b[10001];
int cnt,head[10001];
double f[2601][2600],s[10001],cost[10001],atk[10001],p[10001];
struct node
{
    int to,next;
}a[10001];
void add_edge(int from,int to)
{
    a[++cnt].to=to;
    a[cnt].next=head[from];
    head[from]=cnt;
}
int dfn[10001],times,nxt[10001];
void dfs(int u)
{
    b[u]=1;
    dfn[times]=u;
    cost[times]=s[u];
    atk[times]=p[u];
    int tmp=times;
    times++;
    for(int i=head[u];i;i=a[i].next)dfs(a[i].to);
    nxt[tmp]=times;
}
bool check(double x)//atk[i]-x*cost[i]
{
    for(int i=1;i<=n+1;i++)
    for(int j=0;j<=k;j++)
    f[i][j]=-1e9;
    f[1][0]=0;
    nxt[n]=n+1;
    for(int i=1;i<=n;i++)//dfs序
    {
        for(int j=0;j<=k;j++)f[nxt[i]][j]=max(f[nxt[i]][j],f[i][j]);
        for(int j=0;j<k;j++)f[i+1][j+1]=max(f[i+1][j+1],f[i][j]+atk[i]-x*cost[i]);
    }
//    cout<<"check "<<x<<" "<<f[n+1][k]<<endl;
    if(f[n+1][k]>0.0)return 1;
    return 0;
}
int main()
{
    times++;
    k=r();n=r();
    for(int i=1;i<=n;i++)
    {
        cin>>s[i]>>p[i]>>pre[i];
//        s[i]=r();//费用
//        p[i]=r();//战斗值
//        pre[i]=r();
        if(pre[i])add_edge(pre[i],i);
    }
    for(int i=1;i<=n;i++)if(!b[i])dfs(i);
    double l=0,r=3;
    while(r-l>eps)
    {
        double mid=(l+r)/2.0;
        if(check(mid))l=mid;
        else r=mid;
    }
    printf("%.3lf",l);
}
posted @ 2021-07-13 17:24  lei_yu  阅读(44)  评论(0编辑  收藏  举报