【JSOI2016】最佳团队

22 篇文章 0 订阅
7 篇文章 0 订阅

题目

这里写图片描述

输入

这里写图片描述
1 2
1000 1 0
1 1000 1

输出

这里写图片描述
0.001

数据范围

这里写图片描述


剖解题目

一棵以0为根的树,除了根以外,每一个节点有一个价值与一个花费。若选择一个节点则其父亲也必须选,问如何选择使得总价值与总花费的比值最大。


思路

开头,一眼dp,有点像背包?
结尾,一眼01规划。。。。
发现是棵树,可以在树上背包!
然而发现自己背包并不熟,01规划也从未打过。。。。
然后就想到dfn序,弄下来搞了搞。
结果因为一个傻逼错误调了4个钟。。。。


解法

首先我们二分答案mid。
PiSi>=mid 可化为 PimidSi>=0
那么一个点的性价比为val[i]=p[i]-mid*s[i]。、

1.树上依赖背包dp,寻找最大的联通块的权值。
2.将dfn序弄下来:
设f[i,j]表示序列中前i个点选了j个点所能得到的最大性价比。
转移有两种:
若不选,则转移到f[i+size[dfn[i]]][j]上。
若选,则转移到f[i+1][j+1]上。

时间O(n^2 log n)


代码

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define dou double

using namespace std;

const int maxn=2505;
const dou ep=1e-5;
int next[maxn*2],fre[maxn],go[maxn*2],dad[maxn],dfn[maxn],size[maxn],pos[maxn];
dou s[maxn],p[maxn],f[maxn][maxn],val[maxn],mid;
int n,m,num,id;

void add(int x,int y)
{
    go[++num]=y;
    next[num]=fre[x];
    fre[x]=num;
}
void dfs(int x)
{
    dfn[++id]=x; size[x]=1; pos[x]=id;
    int i=fre[x];
    while (i){
        int u=go[i];
        dfs(u);
        size[x]+=size[u];
        i=next[i];
    }
}
void dp(dou mid)
{
    fo(i,1,n) val[pos[i]]=p[i]-mid*s[i];
    fo(i,1,n+1)
        fo(j,0,m+1) f[i][j]=-1e18;
    fo(i,0,n){
        fo(j,0,min(i,m+1)){
            if (f[i][j]+val[i]>f[i+1][j+1]) {
                f[i+1][j+1]=f[i][j]+val[i];
            }
            if (f[i][j]>f[i+size[dfn[i]]][j]){
                f[i+size[dfn[i]]][j]=f[i][j];
            }

        }
    }
}
int main()
{
    //freopen("T.in","r",stdin);
    scanf("%d%d",&m,&n);
    fo(i,1,n){
        scanf("%lf%lf%d",&s[i],&p[i],&dad[i]);
        add(dad[i],i);
    }
    id=-1;
    dfs(0);
    dou l=0,r=1e4;
    while (r-l>ep){
        mid=(l+r)/2;
        dp(mid);
        if (f[n+1][m+1]>=0) l=mid;
        else r=mid;
    }
    printf("%.3lf\n",mid);
}

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值