1. 程式人生 > >BZOJ_4753_[Jsoi2016]最佳團體_樹形背包+01分數規劃

BZOJ_4753_[Jsoi2016]最佳團體_樹形背包+01分數規劃

AC 也有 clas ont 二分 人的 amp strong string

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

Sample Output

0.001


二分答案x,令t[i]=P[i]-x*S[i]。

然後建立源點S,跑個樹形背包求f[S][K+1]是否大於0即可。

代碼:

#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);
}

BZOJ_4753_[Jsoi2016]最佳團體_樹形背包+01分數規劃