列表

详情


NC20226. [JSOI2016]最佳团体

描述

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

输入描述

输入一行包含两个正整数K和N。 
接下来N行,其中第i行包含3个整数Si,Pi,Ri表示候选人i的招募费用,战斗值和推荐人编号。 
对于100%的数据满足1 ≤ K ≤ N ≤ 2500,0 < "Si,Pi" ≤ 10^4,0 ≤ Ri < i

输出描述

输出一行一个实数,表示最佳比值。答案保留三位小数。

示例1

输入:

1 2
1000 1 0
1 1000 1

输出:

0.001

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++(g++ 7.5.0) 解法, 执行用时: 495ms, 内存消耗: 49700K, 提交时间: 2023-07-26 12:18:51

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

const double EPS=1e-4;
const int N=2505;
const int E=5005;

struct edge {int x,y,next;} e[E];

double f[N][N],tmp[N],v[N];
int size[N],a[N],b[N];
int ls[N],edCnt;

void add_edge(int x,int y) {
	e[++edCnt]=(edge) {x,y,ls[x]}; ls[x]=edCnt;
	e[++edCnt]=(edge) {y,x,ls[y]}; ls[y]=edCnt;
}

void dfs(int now,int fa) {
	size[now]=1; f[now][0]=0; f[now][1]=v[now];
	for (int i=ls[now];i;i=e[i].next) {
		if (e[i].y==fa) continue;
		dfs(e[i].y,now);
		rep(j,1,size[now]+size[e[i].y]) tmp[j]=f[0][N-1];
		rep(j,1,size[now]) rep(k,0,size[e[i].y]) {
			tmp[j+k]=std:: max(tmp[j+k],f[now][j]+f[e[i].y][k]);
		}
		size[now]+=size[e[i].y];
		rep(j,1,size[now]) f[now][j]=tmp[j];
	}
}

int main(void) {
	int n,m; scanf("%d%d",&m,&n);
	rep(i,1,n) {
		int fa; scanf("%d%d%d",&b[i],&a[i],&fa);
		add_edge(fa,i);
	}
	double l,r;
	for (l=0,r=10000;r-l>=EPS;) {
		double mid=(l+r)*0.5;
		fill(f,0xc2);
		rep(i,1,n) v[i]=(double)a[i]-mid*(double)b[i];
		dfs(0,0);
		if (f[0][m+1]>0) l=mid;
		else r=mid;
	}
	printf("%.3lf\n", l);
	return 0;
}

C++14(g++5.4) 解法, 执行用时: 813ms, 内存消耗: 75956K, 提交时间: 2020-01-30 13:24:12

#include<bits/stdc++.h>
using namespace std;
#define see(x) cerr<<(#x)<<"="<<x<<endl;
typedef long long ll;
const int N=3000+100;
const ll inf=1e18;
const double eps=1e-5;
ll v[N],c[N],n,k,siz[N];
vector<int>edge[N];
double mid,dp[N][N];

double dfs(int x) {
    siz[x]=1;
    dp[x][0]=0;
    dp[x][1]=(double)v[x]-(double)c[x]*mid;
    for(auto v:edge[x]) {
        dfs(v);
        for(int i=siz[x];i>=1;i--)
            for(int j=0;j<=siz[v];j++)
                dp[x][i+j]=max(dp[x][i+j],dp[x][i]+dp[v][j]);
        siz[x]+=siz[v];
    }
    if(x==0) return dp[x][k];
}

int main() {
    cin>>k>>n;k++;
    int x;
    for(int i=1;i<=n;i++) {
        scanf("%lld%lld%d",&c[i],&v[i],&x);
        edge[x].push_back(i);
    }
    double L=0,R=1e9,ans=0;
    while(L+eps<=R) {
        mid=(L+R)/2.0;
        memset(dp,-10,sizeof dp);
        if(dfs(0)>=0) ans=mid,L=mid;
        else R=mid;
    }
    printf("%.3f",ans);
}

C++11(clang++ 3.9) 解法, 执行用时: 887ms, 内存消耗: 50020K, 提交时间: 2020-03-28 15:44:48

#include<bits/stdc++.h>
using namespace std;


const int N=2510;
vector<int>G[N];


int sz[N];
double dp[N][N];

double p[N],s[N],tmp[N];

int n,k;
void dfs(int x,double mid)
{
	sz[x]=1;
	dp[x][0]=0;
	dp[x][1]=p[x]-s[x]*mid;
	for(int v:G[x])
	{
		dfs(v,mid);
		for(int i=0;i<=k;i++) tmp[i]=-0x3f3f3f3f;
		for(int i=1;i<=sz[x];i++)//父节点 必须要有 
		{
			for(int j=0;j<=sz[v]&&i+j<=k;j++)
			{
				tmp[i+j]=max(tmp[i+j],dp[x][i]+dp[v][j]);
			}
		}
		for(int i=0;i<=k;i++) dp[x][i]=max(dp[x][i],tmp[i]);
		sz[x]+=sz[v];
	}
}
int main()
{
	cin>>k>>n;
	k++;
	for(int i=1;i<=n;i++)
	{
		int x;
		cin>>s[i]>>p[i]>>x;
		G[x].push_back(i);
	}
	double l=0,r=2e9;
	while(r-l>1e-5)
	{
		double mid=(l+r)/2;
		memset(dp,-0x3f,sizeof dp);
		dfs(0,mid);
		if(dp[0][k]>=0) l=mid;
		else r=mid-0.0001;
	}
	printf("%.3f\n",l);
	return 0; 
}

上一题