[JSOI 2016] 最佳团体(树形背包+01分数规划)

Posted leom10

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[JSOI 2016] 最佳团体(树形背包+01分数规划)相关的知识,希望对你有一定的参考价值。

4753: [Jsoi2016]最佳团体

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 2003  Solved: 790
[Submit][Status][Discuss]

Description

JSOI信息学代表队一共有N名候选人,这些候选人从1到N编号。方便起见,JYY的编号是0号。每个候选人都由一位
编号比他小的候选人Ri推荐。如果Ri=0则说明这个候选人是JYY自己看上的。为了保证团队的和谐,JYY需要保证,
如果招募了候选人i,那么候选人Ri"也一定需要在团队中。当然了,JYY自己总是在团队里的。每一个候选人都有
一个战斗值Pi",也有一个招募费用Si"。JYY希望招募K个候选人(JYY自己不算),组成一个性价比最高的团队。
也就是,这K个被JYY选择的候选人的总战斗值与总招募总费用的比值最大。
 

 

Input

输入一行包含两个正整数K和N。
接下来N行,其中第i行包含3个整数Si,Pi,Ri表示候选人i的招募费用,战斗值和推荐人编号。
对于100%的数据满足1≤K≤N≤2500,0<"Si,Pi"≤10^4,0≤Ri<i
 
 

 

Output

输出一行一个实数,表示最佳比值。答案保留三位小数。
 

 

Sample Input

1 2
1000 1 0
1 1000 1

Sample Output

0.001
题解:
一开始看到这题觉得是道水题,就和树上染色那道题一样,是个经典的$O(n^2)$的树上背包但是这道题不同的是转移有两个要素,并且这两个要素是作比的关系而不是简单的相加,一开始想两个分别转移,但是这两个东西他是同选取的,无法用正常方法维护,然后想了很长时间怎么进行转移,发现很不好转移,一点开标签发现需要一个新芝士点:01分数规划,然而自己不会啊qwq,上网学了一下,看了一下觉得也不是很难,大概介绍一下叭。
01分数规划:

01分数规划,简单的来说,就是有一些二元组$(s_i,p_i)$,从中选取一些二元组,使得$\Sigmas_i/\Sigmap_i$最大(最小)。

这种题一类通用的解法就是,我们假设$x= \Sigmas_i/\Sigmap_i$的最大(小)值,那么就有$x*\Sigmap_i=\Sigmas_i$,即$\Sigmas_i-x*\Sigmap_i=0$。也就是说,当某一个值x满足上述式子的时候,它就是要求的值。我们可以想到枚举……不过再想想,这个可以二分答案。

所以我们直接二分答案,当上述式子>0,说明答案小了,<0则说明答案大了,这样计算即可。


 

好了,前置芝士解决了,那实际上这题就是道01分数规划和$O(n^2)$树形背包的裸题了,还有要注意的就是初始化问题,尤其注意的是每次二分答案都要再给dp数组附上初值,其实每次check的就是dp数组,即dp数组的值就是x。最后要注意的一点是因为他题目中说自己必须选,并且不计入总人数,所以要k++。

总时间复杂度$O(n^2logn)$,稍卡常,luogu上要开O2。

完结撒花。

技术图片
 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<vector>
 7 #include<queue>
 8 #include<cstdlib>
 9 using namespace std;
10 const int N=2505;
11 const double eps=1e-5;
12 int first[N],nex[N<<1],to[N<<1],tot;
13 int vis[N],size[N];
14 int n,k;
15 int att[N],co[N],ri[N];
16 double TerStegen[N];
17 double f[N][N];
18 void add(int a,int b) to[++tot]=b,nex[tot]=first[a],first[a]=tot;
19 void dfs(int x)
20     vis[x]=1;size[x]=1;
21     f[x][1]=TerStegen[x];//
22     for(int i=first[x];i;i=nex[i])
23         int y=to[i];
24         //if(vis[y]) continue;//danxiangbian
25         dfs(y);
26         for(int j=min(size[x],k);j>=1;j--) for(int l=min(size[y],k);l>=1;l--) f[x][l+j]=max(f[x][l+j],f[y][l]+f[x][j]);//dayudengyu1?
27         size[x]+=size[y];
28     
29 
30 int check(double mid)
31     int ju;
32     for(int i=0;i<=2501;i++) for(int j=0;j<=2501;j++) f[i][j]=-88484848;
33     //memset(f,0xcf,sizeof(f));
34     for(int i=0;i<=n;i++) f[i][0]=0.0;
35     for(int i=0;i<=n;i++) TerStegen[i]=1.0*att[i]-1.0*mid*1.0*co[i];
36     dfs(0);
37     //cout<<f[0][k]<<endl;
38     if(f[0][k]>=0) ju=1;
39     else ju=0;
40     return ju;
41 
42 int main()
43     scanf("%d%d",&k,&n);
44     k++;//duliu
45     for(int i=1;i<=n;++i)
46         scanf("%d%d%d",&co[i],&att[i],&ri[i]);
47         add(ri[i],i);
48     
49     double l=0.0,r=2e7*1.0;
50     double ans;
51     while(l+eps<r)
52         double mid=(l+r)/2;
53         //cout<<mid<<" "<<l<<" "<<r<<endl; 
54         if(check(mid)) l=mid;
55         else r=mid;
56     
57     printf("%.3lf",(l+r)/2);
58 
View Code

 

以上是关于[JSOI 2016] 最佳团体(树形背包+01分数规划)的主要内容,如果未能解决你的问题,请参考以下文章

bzoj4753: [Jsoi2016]最佳团体(分数规划+树形依赖背包)

[JSOI2016]最佳团体

解题:JSOI 2016 最佳团体

bzoj4753[Jsoi2016]最佳团体 分数规划+树形背包dp

[JSOI 2016] 最佳团体

bzoj 4753: [Jsoi2016]最佳团体01分数规划+二分+树上背包