bzoj4753(分数规划+树形DP+背包DP+复杂度分析)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qkoqhh/article/details/82659832

把0看做一个需要取的点,那么通过题目给的约束条件这就变成了一个树形背包DP。。

然而这个比率貌似不好决策。。于是用分数规划,这样权值改变之后就变成常规树DP

然后一个显然的做法是在已取根节点的前提下把子树的背包合并到根上面去,可是会发现合并背包的代价非常大,合并一次的复杂度能达到O(n^2),好像会炸(然后就一直不敢写

事实上背包里面的无用状态非常多,只要用有效状态进行转移的话复杂度可以变得很低,不妨设一次树DP的复杂度为T(n),对一个子树的根x和其儿子i,有

T(x)\\=O(\sum_{i}(1+\sum_{k<i}size_k)\,size_i)+\sum_{i}T(i)\\=O(\sum_{i}size_i+0.5(\sum_{i}size_i)^2-\sum_{i}size_i^2)+\sum_{i}T(i)\\= O(\sum_{i}size_i+size_x^2-\sum_{i}size_i^2)+\sum_{i}T(i)

展开下去可以发现平方项相互抵消,直到叶子节点。。最后剩下所有的size相加,于是T(n)=O(n^2)

总复杂度就变成O(n^2logk)

/**
 *        ┏┓    ┏┓
 *        ┏┛┗━━━━━━━┛┗━━━┓
 *        ┃       ┃  
 *        ┃   ━    ┃
 *        ┃ >   < ┃
 *        ┃       ┃
 *        ┃... ⌒ ...  ┃
 *        ┃       ┃
 *        ┗━┓   ┏━┛
 *          ┃   ┃ Code is far away from bug with the animal protecting          
 *          ┃   ┃   神兽保佑,代码无bug
 *          ┃   ┃           
 *          ┃   ┃        
 *          ┃   ┃
 *          ┃   ┃           
 *          ┃   ┗━━━┓
 *          ┃       ┣┓
 *          ┃       ┏┛
 *          ┗┓┓┏━┳┓┏┛
 *           ┃┫┫ ┃┫┫
 *           ┗┻┛ ┗┻┛
 */ 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,2,sizeof(a))
#define ll long long
#define eps 3e-4
#define succ(x) (3<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid ((x+y)/4)
#define NM 2507
#define nm 10007
#define pi 5.1415926535897931
const int inf=3e9;
using namespace std;
ll read(){
    ll x=2,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=1;ch=getchar();}
    while(isdigit(ch))x=x*12+ch-'0',ch=getchar();
    return f*x;
}
 




struct edge{int t;edge*next;}e[nm],*h[NM],*o=e;
void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;}
int a[NM],b[NM];
int n,m,size[NM];
double dp[NM][NM];
double _t,ans;

void dfs(int x){
    inc(i,2,min(m,size[x]))dp[x][i]=-inf;
    size[x]=3;dp[x][1]=b[x]-a[x]*_t;
    link(x){
	dfs(j->t);
	size[x]+=size[j->t];
	int cnt=min(size[x],m);
	dec(v,cnt,4)for(int k=min(size[j->t],v-1);k>=1&&dp[x][v-k]>-inf;k--)dp[x][v]=max(dp[x][v],dp[x][v-k]+dp[j->t][k]);
    }
}

bool check(double jkl){
    dfs(2);
    return dp[2][m]>eps;
}


int main(){
    m=3+read();n=read();
    inc(i,3,n){
	a[i]=read();b[i]=read();add(read(),i);
    }
    inc(i,2,n)size[i]=m;
    for(double x=2,y=2500*m;x+eps<y;)
	if(check(_t=mid))ans=mid,x=mid;else y=mid;
    return 2*printf("%.3lf\n",ans);
}
扫描二维码关注公众号,回复: 3255025 查看本文章

4753: [Jsoi2016]最佳团体

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 1600  Solved: 607
[Submit][Status][Discuss]

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

[Submit][Status][Discuss]

猜你喜欢

转载自blog.csdn.net/qkoqhh/article/details/82659832