BZOJ 4753: [Jsoi2016]最佳团体 树形背包 01分数规划

4753: [Jsoi2016]最佳团体

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 583  Solved: 242
[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

分数规划就不多说了,二分就行

比较好的就是这个n^2的树形背包

详见这里


#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<set>
#include<map>
using namespace std;

typedef double db;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}
void print(int x)
{if(x<0)x=-x,putchar('-');if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=2510;
const db eps=1e-7;

int ecnt,last[N];
struct EDGE{int to,nt;}e[N<<1];
inline void add(int u,int v)
{e[++ecnt]=(EDGE){v,last[u]};last[u]=ecnt;}

int n,K,S[N],P[N];

db a[N],f[N][N];

int size[N];

void dfs(int u)
{
	size[u]=1;
	for(int i=last[u];i;i=e[i].nt)
	{dfs(e[i].to);size[u]+=size[e[i].to];}
}

void solve(int u)
{
	memset(f[u],0Xc2,sizeof(f[u]));
	int tot=0,b=0;
	if(u)f[u][1]=a[u],tot++,b++;else f[u][0]=0;
	for(int i=last[u];i;i=e[i].nt)
	{
		solve(e[i].to);
		for(int j=tot;j>=b;--j)for(int k=1;k<=size[e[i].to];++k)
		f[u][j+k]=max(f[u][j+k],f[u][j]+f[e[i].to][k]);
		tot+=size[e[i].to];
	}
}

inline bool judge(db x)
{
	register int i;
	for(i=1;i<=n;++i)a[i]=P[i]-S[i]*x;
	solve(0);
	return f[0][K]>0-eps;
}

int main()
{
	K=read();n=read();
	register int i,x;
	db l=0,r=0,mid;
	for(i=1;i<=n;++i)S[i]=read(),P[i]=read(),x=read(),add(x,i),r=max(double(P[i]),r);
	dfs(0);
	while(r-l>eps)
	{
		mid=(l+r)/2;
		judge(mid)?l=mid:r=mid;
	}
	printf("%.3lf\n",l);
}
/*
1 2
1000 1 0
1 1000 1

0.001
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值