JSOI信息学代表队一共有N名候选人,这些候选人从1到N编号。方便起见,JYY的编号是0号。每个候选人都由一位
编号比他小的候选人Ri推荐。如果Ri=0则说明这个候选人是JYY自己看上的。为了保证团队的和谐,JYY需要保证,
如果招募了候选人i,那么候选人Ri"也一定需要在团队中。当然了,JYY自己总是在团队里的。每一个候选人都有
一个战斗值Pi",也有一个招募费用Si"。JYY希望招募K个候选人(JYY自己不算),组成一个性价比最高的团队。
也就是,这K个被JYY选择的候选人的总战斗值与总招募总费用的比值最大。
输入一行包含两个正整数K和N。
接下来N行,其中第i行包含3个整数Si,Pi,Ri表示候选人i的招募费用,战斗值和推荐人编号。
对于100%的数据满足1≤K≤N≤2500,0<"Si,Pi"≤10^4,0≤Ri<i
输出一行一个实数,表示最佳比值。答案保留三位小数。
01分数规划,简单的来说,就是有一些二元组$(s_i,p_i)$,从中选取一些二元组,使得$\Sigma{s_i}/\Sigma{p_i}$最大(最小)。
这种题一类通用的解法就是,我们假设$x= \Sigma{s_i}/\Sigma{p_i}$的最大(小)值,那么就有$x*\Sigma{p_i}=\Sigma{s_i}$,即$\Sigma{s_i}-x*\Sigma{p_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