• Luogu P4322 [JSOI2016]最佳团体


    JZdalao昨天上课讲的题目,话说JSOI的题目是真的不难ZJOI的题目真的是虐啊!

    题意很简单,抽象一下就是:有一棵树,一次只能选从根到某个节点上的链上的所有点,问从中取出k个节点所得到的总价值和总代价的比最大是多少。

    像这种比值最大的题目,很容易让人联想到分数规划

    关于分数规划的姿势,可以自行百度

    对于题意进行进一步分析,得到要求的是:

    max{∑v[i]/∑w[i]}(if i is chosen)
    

    因此我们设x,二分x,看看是否存在:

    max{∑v[i]/∑w[i]}>=x(if i is chosen)
    

    移项得每个点的权值c[i]:

    c[i]=v[i]-x*w[i](1<=i<=n)
    

    所以我们得出c[i],然后看看能否在树上找出满足题意的点集使得点权和大于等于0

    所以要找出最大值,再考虑树形DP

    但是一般的背包DP和多叉树转二叉树DP都是O(n^3)的

    所以可以用DFS序来完成这种树上的n个点选m个点的DP

    具体如下:

    1. 预处理出原树的DFS序,以及每个节点的子树大小

    2. 进行DP 用f[i][j]表示DFS序列中前i-1个点中选取j个点中得到的最大点权和

    3. 对于每一个f[i][j],可以转移:

    • f[i+1][j+1]=max(f[i+1][j+1],f[i][j]+c[s[i]]) (选这个点)

    • f[i+size[s[i]]][j]=max(f[i+size[s[i]]][j],f[i][j]) (不选这个点,则直接跳过整棵子树

    最后判断f[n+1][k]是否大于等于0

    注意这里的人数不包括根节点,因此k要加1

    CODE

    // luogu-judger-enable-o2
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef double DB;
    const int N=2505;
    const DB EPS=1e-5,INF=1e9;
    struct edge
    {
        int to,next;
    }e[N];
    int head[N],v[N],w[N],size[N],s[N],n,k,x,cnt,tot;
    DB f[N][N],c[N];
    inline char tc(void)
    {
        static char fl[100000],*A=fl,*B=fl;
        return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
        x=0; char ch=tc();
        while (ch<'0'||ch>'9') ch=tc();
        while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
    }
    inline void add(int x,int y)
    {
        e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;
    }
    inline void DFS(int now)
    {
        s[++tot]=now; size[now]=1;
        for (register int i=head[now];i!=-1;i=e[i].next)
        DFS(e[i].to),size[now]+=size[e[i].to];
    }
    inline DB max(DB a,DB b)
    {
        return a>b?a:b;
    }
    inline bool check(DB x)
    {
        register int i,j;
        for (i=1;i<=n;++i)
        c[i]=(DB)v[i]-(DB)x*w[i];
        for (i=1;i<=tot+1;++i)
        for (j=0;j<=k;++j)
        f[i][j]=-INF;
        for (f[1][0]=0,i=1;i<=tot;++i)
        for (j=0;j<=k;++j)
        {
            f[i+1][j+1]=max(f[i+1][j+1],f[i][j]+c[s[i]]);
            f[i+size[s[i]]][j]=max(f[i+size[s[i]]][j],f[i][j]);
        }
        return f[tot+1][k]>=0;
    }
    int main()
    {
        register int i;
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
        memset(e,-1,sizeof(e));
        memset(head,-1,sizeof(head));
        read(k); read(n); ++k;
        for (i=1;i<=n;++i)
        {
            read(w[i]); read(v[i]); read(x);
            add(x,i);
        }
        DFS(0);
        DB l=0,r=1e4;
        while (r-l>EPS)
        {
            DB mid=(DB)(l+r)/2;
            if (check(mid)) l=mid; else r=mid;
        }
        printf("%.3lf",l);
        return 0;
    }
    
  • 相关阅读:
    go语言基本语法
    go语言学习
    Dockerfile简单实战
    Dockerfile构建命令
    docker安装prometheus grafana监控
    docker kali安装及复现永恒之蓝漏洞
    Windows系统实用快捷键
    python pip修改国内源
    网络基础——子网掩码及网络划分
    OSI七层模型简单概念及相关面试题
  • 原文地址:https://www.cnblogs.com/cjjsb/p/8822155.html
Copyright © 2020-2023  润新知