zoukankan      html  css  js  c++  java
  • [JSOI2016]最佳团体(01分数规划+树形背包)

    [JSOI2016]最佳团体(01分数规划+树形背包)

    题面

    JSOI信息学代表队一共有N名候选人,这些候选人从1到N编号。方便起见,JYY的编号是0号。每个候选人都由一位编号比他小的候选人Ri推荐。如果Ri=0则说明这个候选人是JYY自己看上的。为了保证团队的和谐,JYY需要保证,如果招募了候选人i,那么候选人Ri"也一定需要在团队中。当然了,JYY自己总是在团队里的。每一个候选人都有一个战斗值Pi",也有一个招募费用Si"。JYY希望招募K个候选人(JYY自己不算),组成一个性价比最高的团队。也就是,这K个被JYY选择的候选人的总战斗值与总招募总费用的比值最大。
    (n,K leq 2500)

    分析

    01分数规划的套路,先二分答案mid,每个点权值变成(P_i-midcdot S_i),那么问题就变成了在树上选择一个大小为(K+1)的连通块,求最大权值,如果最大权值大于0,就说明答案大于(mid).直接套树形背包的板子:

    for(int i=0;i<=n;i++){
        for(int j=0;j<=min(i,k+1);j++){
            dp[nex[i]][j]=max(dp[nex[i]][j],dp[i][j]);
            dp[i+1][j+1]=max(dp[i+1][j+1],dp[i][j]+p[hash_dfn[i]]-mid*s[hash_dfn[i]]);
        }
    }
    return dp[n+1][k+1]>=eps;
    

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxn 3000 
    #define eps 1e-5
    #define INF 1e18
    using namespace std;
    typedef  double db;
    int n,k;
    struct edge{
    	int from;
    	int to;
    	int next;
    }E[maxn*2+5];
    int head[maxn+5];
    int sz=1;
    void add_edge(int u,int v){
    	sz++;
    	E[sz].from=u;
    	E[sz].to=v;
    	E[sz].next=head[u];
    	head[u]=sz; 
    }
    
    int tim;
    int dfn[maxn+5];
    int hash_dfn[maxn+5];
    int nex[maxn+5];
    int s[maxn+5],p[maxn+5]; 
    db dp[maxn+5][maxn+5];
    void dfs(int x){
    	dfn[x]=tim++;
    	hash_dfn[dfn[x]]=x;
    	for(int i=head[x];i;i=E[i].next){
    		int y=E[i].to;
    		dfs(y); 
    	}
    	nex[dfn[x]]=tim;
    }
    
    bool check(db mid){
    	for(int i=1;i<=n+1;i++){
    		for(int j=0;j<=k+1;j++){
    			dp[i][j]=-INF;
    		}
    	}
    
    	for(int i=0;i<=n;i++){
    		for(int j=0;j<=min(i,k+1);j++){
    			dp[nex[i]][j]=max(dp[nex[i]][j],dp[i][j]);
    			dp[i+1][j+1]=max(dp[i+1][j+1],dp[i][j]+p[hash_dfn[i]]-mid*s[hash_dfn[i]]);
    		}
    	}
    	return dp[n+1][k+1]>=eps; 
    
    }
    
    int main(){
    	int f;
    	scanf("%d %d",&k,&n);	
    	for(int i=1;i<=n;i++){
    		scanf("%d %d %d",&s[i],&p[i],&f);
    		add_edge(f,i);
    	}
    	dfs(0);
    	db l=0,r=1e4;
    	db mid;
    	db ans=0;
    	while(r-l>=eps){
    		mid=(l+r)/2;
    		if(check(mid)){
    			ans=mid;
    			l=mid+eps;
    		}else r=mid-eps; 
    	}
    	printf("%.3lf
    ",ans);
    } 
    
  • 相关阅读:
    提高PHP程序运行效率的方法
    必须知道的sql编写技巧。多条件查询不拼接字符串·的写法
    数据库SQL优化大总结之 百万级数据库优化方案
    编程一开始就应该养成的好习惯
    php图像处理
    jqurey 简单的,我也简单
    菜单上下级 (全国地区)
    think php v5.0
    正则表达式
    有感赠朵朵
  • 原文地址:https://www.cnblogs.com/birchtree/p/12926107.html
Copyright © 2011-2022 走看看