P4322 [JSOI2016]最佳团体 题解
两个知识点
第一个是01分数规划
第二个是树形背包,树形背包请看另一篇博客
01分数规划主要用于求如柿子 $\huge\frac{\sum a_i}{\sum b_i}$的最大值
那么设这个柿子=k,则有 $\sum a_i-k\sum b_i≥0$ 如果说是一个序列,那么我们可以二分k值,直接对所有元素用$a-kb$的值进行排序,选择其中最大的x个判断其是否大于0
但是这个题不是一个序列,还有树关系。 其实也很简单,套上树形背包即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const double eps=1e-7;
inline int r()
{
int s=0,k=1;char c=getchar();
while(!isdigit(c))
{
if(c=='-')k=-1;
c=getchar();
}
while(isdigit(c))
{
s=s*10+c-'0';
c=getchar();
}
return s*k;
}
int n,k,pre[10001],b[10001];
int cnt,head[10001];
double f[2601][2600],s[10001],cost[10001],atk[10001],p[10001];
struct node
{
int to,next;
}a[10001];
void add_edge(int from,int to)
{
a[++cnt].to=to;
a[cnt].next=head[from];
head[from]=cnt;
}
int dfn[10001],times,nxt[10001];
void dfs(int u)
{
b[u]=1;
dfn[times]=u;
cost[times]=s[u];
atk[times]=p[u];
int tmp=times;
times++;
for(int i=head[u];i;i=a[i].next)dfs(a[i].to);
nxt[tmp]=times;
}
bool check(double x)//atk[i]-x*cost[i]
{
for(int i=1;i<=n+1;i++)
for(int j=0;j<=k;j++)
f[i][j]=-1e9;
f[1][0]=0;
nxt[n]=n+1;
for(int i=1;i<=n;i++)//dfs序
{
for(int j=0;j<=k;j++)f[nxt[i]][j]=max(f[nxt[i]][j],f[i][j]);
for(int j=0;j<k;j++)f[i+1][j+1]=max(f[i+1][j+1],f[i][j]+atk[i]-x*cost[i]);
}
// cout<<"check "<<x<<" "<<f[n+1][k]<<endl;
if(f[n+1][k]>0.0)return 1;
return 0;
}
int main()
{
times++;
k=r();n=r();
for(int i=1;i<=n;i++)
{
cin>>s[i]>>p[i]>>pre[i];
// s[i]=r();//费用
// p[i]=r();//战斗值
// pre[i]=r();
if(pre[i])add_edge(pre[i],i);
}
for(int i=1;i<=n;i++)if(!b[i])dfs(i);
double l=0,r=3;
while(r-l>eps)
{
double mid=(l+r)/2.0;
if(check(mid))l=mid;
else r=mid;
}
printf("%.3lf",l);
}
本文来自博客园,作者:lei_yu,转载请注明原文链接:https://www.cnblogs.com/lytql/p/15007493.html