BZOJ_4753_[Jsoi2016]最佳团体_树形背包+01分数规划
BZOJ_4753_[Jsoi2016]最佳团体_树形背包+01分数规划
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
1000 1 0
1 1000 1
Sample Output
0.001
二分答案x,令t[i]=P[i]-x*S[i]。
然后建立源点S,跑个树形背包求f[S][K+1]是否大于0即可。
按子树合并的树形背包复杂度是$O(n^2)$的。
代码:
#include <cstdio> #include <string.h> #include <algorithm> using namespace std; typedef double f2; #define N 2550 #define S (n+1) #define eps 1e-6 #define inf 1000000000 int head[N],to[N<<1],nxt[N<<1],cnt,n,K,A[N],B[N],C[N],siz[N]; f2 t[N],g[N],f[N][N]; inline void add(int u,int v) { to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; } void dfs(int x) { siz[x]=1;int i,j,k; f[x][1]=t[x]; for(k=head[x];k;k=nxt[k]) { dfs(to[k]); for(i=1;i<=siz[x]+siz[to[k]];i++) g[i]=f[x][i]; for(i=1;i<=siz[x];i++) if(f[x][i]>-inf) { for(j=1;j<=siz[to[k]];j++) if(f[to[k]][j]>-inf) { g[i+j]=max(g[i+j],f[x][i]+f[to[k]][j]); } } for(i=1;i<=siz[x]+siz[to[k]];i++) f[x][i]=g[i]; siz[x]+=siz[to[k]]; } } bool check(f2 x) { int i,j; for(i=0;i<=n;i++) t[i]=B[i]-x*A[i]; t[S]=0; for(i=1;i<=S;i++) { for(j=1;j<=K+1;j++) { f[i][j]=-inf; } } dfs(S); return f[S][K+1]>eps; } int main() { //freopen("sales.in","r",stdin); //freopen("sales.out","w",stdout); scanf("%d%d",&K,&n); int i; f2 sum=0; for(i=1;i<=n;i++) { scanf("%d%d%d",&A[i],&B[i],&C[i]); if(!C[i]) C[i]=S; add(C[i],i); sum+=A[i]; } f2 l=0,r=10000; while(r-l>eps) { f2 mid=(l+r)/2; if(check(mid)) l=mid; else r=mid; } printf("%.3f\n",l); }