[树形dp][二分] Jzoj P4512 最佳团队
题解
- 题目大意:在一棵树上,找到一棵树使他们费用和比贡献和的比值最小
- 比值最小,也差不多是最大值最小的意思,那么就可以考虑二分一个值,然后就是常规操作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 }