bzoj4753 最佳团体
题目描述
JSOI 信息学代表队一共有 NN 名候选人,这些候选人从 11 到 NN 编号。方便起见,JYY 的编号是 00 号。每个候选人都由一位编号比他小的候选人R_iRi 推荐。如果 R_i = 0Ri=0,则说明这个候选人是 JYY 自己看上的。
为了保证团队的和谐,JYY 需要保证,如果招募了候选人 ii,那么候选人 R_iRi 也一定需要在团队中。当然了,JYY 自己总是在团队里的。每一个候选人都有一个战斗值 P_iPi ,也有一个招募费用 S_iSi 。JYY 希望招募 KK 个候选人(JYY 自己不算),组成一个性价比最高的团队。也就是,这 KK 个被 JYY 选择的候选人的总战斗值与总招募费用的比值最大。
输入输出格式
输入格式:
输入一行包含两个正整数 KK 和 NN 。
接下来 NN 行,其中第 ii 行包含三个整数 S_iSi , P_iPi , R_iRi , 表示候选人 ii 的招募费用,战斗值和推荐人编号。
输出格式:
输出一行一个实数,表示最佳比值。答案保留三位小数。
这题涉及到了比值最大,最好用分数规划来解决。
我们需要求出pi和ri的比值最大,不妨设Σpi/Σri>=x ,经过转移Σpi>=Σri*x => Σpi-Σri*x>=0. 由此可见,我们可以二分出来一个x使这个值>=0.
然后我们可以用树形DP来计算出最优值。
我们先DFS一遍得到每个树上节点的DFS序(时间戳),令f[i][j]为DFS序为i的点,取j个的最优值。
如果当前点取,说明自己的子树也可以取,所以f[i+1][j+1]=max(f[i+1][j+1],f[i][j]+val[i]);
如果当前点不取,说明要取到下一颗树,我们记录size[i]代表以i为根的子树的大小。根据DFS序的性质,我们知道下一颗和自己平行的子树的DFS序为i+size.
所以转移方程是:f[i+size[i]][j]=max(f[i+size[i]][j],f[i][j]);
// luogu-judger-enable-o2 #include <iostream> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cstring> #include <stack> #define in(a) a=read() #define MAXN 200020 #define REP(i,k,n) for(int i=k;i<=n;i++) using namespace std; inline int read(){ int x=0,f=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } int k,n; int INF=99999999; double p[2510],s[2510]; int total=0,to[2520],nxt[2510],head[2510]; int cnt=0,dfn[2510],ind[2510],size[2510]; double f[2510][2510],val[2510]; inline void adl(int a,int b){ total++; to[total]=b; nxt[total]=head[a]; head[a]=total; return ; } inline void DFS(int u){ dfn[u]=cnt; ind[cnt++]=u; size[u]=1; for(int e=head[u];e;e=nxt[e]){ DFS(to[e]); size[u]+=size[to[e]]; } return ; } inline double DP(double x){ //cout<<x<<endl; REP(i,1,n){ val[i]=p[ind[i]]-x*s[ind[i]]; //cout<<ind[i]<<" "<<val[i]<<endl; } REP(i,1,n+1) REP(j,0,k+1) f[i][j]=-INF; REP(i,0,n) REP(j,0,min(i,k+1)){ f[i+1][j+1]=max(f[i+1][j+1],f[i][j]+val[i]); f[i+size[ind[i]]][j]=max(f[i+size[ind[i]]][j],f[i][j]); } /*REP(i,0,n){ REP(j,0,min(i,k+1)) cout<<f[i][j]<<" "; cout<<endl; }*/ return f[n+1][k+1]; } int main(){ in(k);in(n); int a; double maxn=-INF; REP(i,1,n){ scanf("%lf%lf%d",&s[i],&p[i],&a); adl(a,i); maxn=max(maxn,p[i]); } DFS(0); val[0]=0.0; double left=0.0,right=maxn; while(right-left>0.00001){ double mid=(left+right)/2.0; //cout<<left<<" "<<right<<" "<<mid<<endl; if(DP(mid)>=0.00001) left=mid; else right=mid; } printf("%.3lf",left); return 0; } /* 2 4 1 2 0 2 2 0 1 3 1 2 3 1 */