题解
- 题目大意:在一棵树上,找到一棵树使他们费用和比贡献和的比值最小
- 比值最小,也差不多是最大值最小的意思,那么就可以考虑二分一个值,然后就是常规操作dp判断
- 根据题目来看很显然就是一个树上背包
- 设f[i][j]为以i为根的树里选择j个点的(贡献和-费用和*二分比值)
- 显然如果最后dp完后,f[0][k+1]>0的话,就是成立的
- 考虑转移,那么就可以枚举两个值,一个是当前子树选点的个数,一个是当前点的儿子的子树选点的个数,转移方程显然
代码
1 #include <cstdio>
2 #include <iostream>
3 #define N 2510
4 #define eps 1e-5
5 using namespace std;
6 struct edge { int to,from; }e[N];
7 int k,n,bz[N],p[N],s[N],ans,t;
8 double f[N][N],l,r,mid;
9 char ch;
10 void insert(int x,int y) { e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt; }
11 void dp(int x,int y)
12 {
13 bz[x]=1;
14 if (y==0) return;
15 for (int i=head[x];i;i=e[i].from)
16 {
17 dp(e[i].to,y-1),bz[x]+=bz[e[i].to];
18 for (int j=min(y,bz[x]);j;j--)
19 for (int k=min(j,bz[e[i].to]);k;k--)
20 f[x][j]=max(f[x][j],f[e[i].to][k]+f[x][j-k]);
21 }
22 for (int i=min(y,bz[x]);i;i--) f[x][i]=f[x][i-1]+p[x]-s[x]*mid;
23 }
24 int main()
25 {
26 scanf("%d%d",&k,&n);
27 for (int i=1,x;i<=n;i++) scanf("%d%d%d",&s[i],&p[i],&x),insert(x,i);
28 l=0,r=3;
29 while (r-l<eps)
30 {
31 mid=l+r>>1;
32 memset(f,127,sizeof(f));
33 for (int i=0;i<=n;i++) f[i][0]=0;
34 dp(0,k+1);
35 if (f[0][k+1]) l=mid+eps; else r=mid-eps;
36 }
37 printf("%.3lf",l+r>>1);
38 }