• [bzoj4753][Jsoi2016]最佳团体【0/1分数规划】【dp】


    【题目描述】

    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

    HINT

    2017.9.12新加数据一组 By GXZlegend

    Source

    【题解】

     题目大意是一棵树,每个点有价值和花费,要选择一个包含根节点的连通块,使 价值和A/花费和B 最大。

    0/1分数规划套路题,变式得 A=ans*B,显然可以二分ans,将每个的权值设为a-ans*b,再在新树上判断是否有可行的连通块使权值和 >0,dp一下就行了

    若存在,则说明当前二分的ans可以被满足,否则不行。

    /* --------------
        user Vanisher
        problem bzoj-4753 
    ----------------*/
    # include <bits/stdc++.h>
    # define 	ll 		long long
    # define 	inf 	1e9
    # define 	eps 	1e-6
    # define 	N 		3010
    using namespace std;
    int read(){
    	int tmp=0, fh=1; char ch=getchar();
    	while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    	while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    	return tmp*fh;
    }
    struct node{
    	int data,next;
    }e[N];
    int place,head[N],size[N],k,n,fa[N];
    double a[N],b[N],f[N][N],g[N];
    void build(int u, int v){
    	e[++place].data=v; e[place].next=head[u]; head[u]=place;
    }
    void dp(int x, double p){
    	size[x]=1; f[x][0]=0, f[x][1]=a[x]-p*b[x];
    	for (int ed=head[x]; ed!=0; ed=e[ed].next){
    		dp(e[ed].data,p);
    		for (int i=0; i<=size[x]+size[e[ed].data]; i++) g[i]=-inf;
    		g[0]=0;
    		for (int i=1; i<=size[x]; i++)
    			for (int j=0; j<=size[e[ed].data]; j++)
    				g[i+j]=max(g[i+j],f[e[ed].data][j]+f[x][i]);
    		size[x]=size[x]+size[e[ed].data];
    		for (int i=0; i<=size[x]; i++)
    			f[x][i]=g[i];
    	}
    }
    double check(double p){
    	dp(1,p);
    	return f[1][k];
    }
    int main(){
    	k=read()+1, n=read()+1;
    	for (int i=2; i<=n; i++){
    		b[i]=read(), a[i]=read(), fa[i]=read()+1;
    		build(fa[i],i);
    	}
    	double pl=0, pr=2e4, ans=0;
    	while (pl+eps<=pr){
    		double mid=(pl+pr)/2;
    		if (check(mid)>=0)
    			ans=mid, pl=mid+eps;
    			else pr=mid-eps;
    	}
    	printf("%.3lf
    ",ans);
    	return 0;
    }
    



  • 相关阅读:
    线程
    开启程序子进程的方式
    multiprocess模块
    计算机网络小知识
    解决粘包问题
    网络编程相关
    反射与元类
    多态相关
    封装相关与接口
    类的继承和组合
  • 原文地址:https://www.cnblogs.com/Vanisher/p/9136032.html
Copyright © 2020-2023  润新知