BZOJ 4753 二分+树形DP

思路:

先二分答案

f[x][j]表示在x的子树里选j个点

f[x][j+k]=max(f[x][j+k],f[x][j]+f[v[i]][k]);

初始化

x!=0 -> f[x][1]=p[x]-s[x]*mid

x=0 -> f[x][0]=0

 类似4033的那样转移 看似O(n^3)实际O(n^2)

加一个二分 复杂度O(能过)

//By SiriusRen
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=2505;
int n,K,s[N],p[N],r[N],first[N],next[N],v[N],tot,size[N];
double f[N][N],mid;
void add(int x,int y){v[tot]=y,next[tot]=first[x],first[x]=tot++;}
void dfs(int x){
    if(x){size[x]=1;f[x][1]=p[x]-s[x]*mid;}
    else size[0]=0,f[0][0]=0;
    for(int i=first[x];~i;i=next[i]){
        dfs(v[i]);
        for(int j=size[x];j>=0;j--){
            for(int k=size[v[i]];k>=0;k--){
                f[x][j+k]=max(f[x][j+k],f[x][j]+f[v[i]][k]);
            }
        }
        size[x]+=size[v[i]];
    }
}
int main(){
    memset(first,-1,sizeof(first));
    scanf("%d%d",&K,&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&s[i],&p[i],&r[i]);
        add(r[i],i);
    }
    double l=0,r=0x3f3f3f;
    while(r-l>1e-5){
        for(int i=0;i<=n;i++)
            for(int j=0;j<=n;j++)
                f[i][j]=-0x3f3f3f;
        mid=(l+r)/2;
        dfs(0);
        if(f[0][K]>0)l=mid;
        else r=mid;
    }
    printf("%.3lf\n",l);
}

 

posted @ 2017-03-28 22:52  SiriusRen  阅读(228)  评论(0编辑  收藏  举报