(CodeForces)D - Programming Team Gym - 101002D (树型依赖背包)

题目biu

树型依赖背包:传送门

题意:n个员工,要选k个员工,每个员工都有一个薪水和能力,要使得最终性价比最高。

由于这题要选上子节点就必须把父节点选上,我们很容易就想到了树型依赖背包。a[i]:代表能力,b[i]代表薪水

我们可以通过枚举一个答案mid,可以判断  ,来进行check。我们将公式进行简单变换就是这样,我们可以将(a[i]-mid*b[i])看成第i件物品的价值,而i件物品的重量就是1,在满足去子节点就要取上父亲节点的前提下,取上个k+1个物品的最大收益。


#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define SZ(a) int((a).size())
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define drep(i,a,b)	for(int i=a;i>=b;--i)
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const double eps=1e-9;
const int maxn=3e3+5;
//il int add(int x,int y) {return x+y>=mod?x+y-mod:x+y;}
//il int mul(ll x,int y) {return x*y>=mod?x*y%mod:x*y;}
int k,n,a[maxn],b[maxn],w[maxn];
vector<int> eg[maxn];
int sz[maxn],st[maxn],dl[maxn],t=0;
il void dfs(int p){
	sz[p]=1,st[p]=t++,dl[t-1]=p;
	for(int to:eg[p]) dfs(to),sz[p]+=sz[to];
}
double v[maxn],dp[maxn][maxn];
il bool ck(double ta){
	rep(i,1,n) v[i]=1.0*a[i]-1.0*b[i]*ta;
	rep(i,0,n+2) rep(p,0,k+1) dp[i][p]=dp[i][p]=-inf;
	dp[n+1][0]=0.0;
	for(int i=n;i>=0;--i){
		int x=dl[i];
		for(int p=0;p<=k+1;++p){
			if(p<w[x])	dp[i][p]=max(dp[i+sz[x]][p],dp[i][p]);	
			else	dp[i][p]=max(max(dp[i+sz[x]][p],dp[i+1][p-w[x]]+v[x]),dp[i][p]);	
		}
	}
	if(dp[0][k+1]-0.0>eps) return true;
	else return false;
}
int main(){
	SC(k,n);
	int x;
	rep(i,1,n){
		SC(b[i],a[i]),sc(x);
		eg[x].pb(i);
	}
	dfs(0);
	rep(i,0,n) w[i]=1;
	double le=0.0,ri=inf,ans;
	while(abs(ri-le)>eps){
		double mid=(ri+le)/2;
		if(ck(mid)){
			ans=mid;
			le=mid;
		}
		else ri=mid;
	}
	printf("%.3lf\n",ans);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值