版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/88895869
题目大意:
给定一棵树,
个节点。每个节点有
和
两个值。要求选出若干节点,保证某个节点被选,他的父亲一定被选。
使
最大化。
分析:
考虑分数规划,二分一个
,
当
合法时,有
即
也就是
把点的权值设为
,考虑树上dp。设
表示
的子树中选
个的最大值,看是否≥0。
转移要使用枚举
的方法,dp复杂度是
的。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
const int maxn=2507;
const double esp=1e-5;
using namespace std;
int n,m,cnt,x;
double l,r,mid,ans;
int a[maxn],b[maxn],size[maxn],ls[maxn];
double f[maxn][maxn],tmp[maxn],val[maxn];
struct edge{
int y,next;
}g[maxn*2];
void add(int x,int y)
{
g[++cnt]=(edge){y,ls[x]};
ls[x]=cnt;
}
void dfs(int x,int fa)
{
size[x]=1,f[x][0]=0,f[x][1]=val[x];
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
dfs(y,x);
for (int j=1;j<=size[x]+size[y];j++) tmp[j]=f[x][j];
for (int j=1;j<=size[x];j++)
{
for (int k=0;k<=size[y];k++)
{
tmp[j+k]=max(tmp[j+k],f[x][j]+f[y][k]);
}
}
size[x]+=size[y];
for (int j=1;j<=size[x];j++) f[x][j]=max(f[x][j],tmp[j]);
}
}
int main()
{
scanf("%d%d",&m,&n);
m++;
for (int i=1;i<=n;i++)
{
scanf("%d%d%d",&b[i],&a[i],&x);
add(x,i);
}
l=0,r=1e8;
while (r-l>esp)
{
mid=(l+r)/2;
for (int i=1;i<=n;i++) val[i]=(double)a[i]-b[i]*mid;
for (int i=0;i<=n;i++)
{
for (int j=0;j<=m;j++) f[i][j]=-0x3f3f3f3f;
}
dfs(0,0);
if (f[0][m]>=0) l=mid,ans=mid;
else r=mid;
}
printf("%.3lf",ans);
}