码迷,mamicode.com
首页 > Web开发 > 详细

bzoj4753: [Jsoi2016]最佳团体(分数规划+树形依赖背包)

时间:2017-10-19 10:32:49      阅读:231      评论:0      收藏:0      [点我收藏+]

标签:1.0   one   答案   ==   hid   iostream   alt   line   ring   

  菜菜推荐的“水题”虐了我一天T T...(菜菜好强强qwq~

  显然是个分数规划题,二分答案算出p[i]-mid*s[i]之后在树上跑依赖背包,选k个最大值如果>0说明还有更优解。

  第一次接触树形依赖背包,所以之前写的十几发WA和TLE都是错误写法,我还是naive啊T T

  树形依赖背包的普遍做法是按dfs序DP,设f[i][j]为dfs序为i的点,已经选了j个点的最大价值,nxt[i]为i的下一个子树的dfs序则有:

  f[nxt[i]][j]=f[i][j] 

  f[i+1][j+1]=f[i][j]+w[i]

  注意树形依赖背包最好使用刷表法,因为如果要求代价必须为k并且权值有负数的话使用填表法可能会导致从不合法状态转移。

技术分享
#include<iostream> 
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath> 
#include<algorithm> 
using namespace std;
const int maxn=2510,inf=1e9;
struct poi{int too,pre;}e[maxn];
int n,k,x,tot,cnt,mx;
int dfn[maxn],nxt[maxn],last[maxn],s[maxn],p[maxn];
double f[maxn][maxn],w[maxn];
inline void read(int &k)
{
    int f=1;k=0;char c=getchar();
    while(c<0||c>9)c==-&&(f=-1),c=getchar();
    while(c<=9&&c>=0)k=k*10+c-0,c=getchar();
    k*=f;
}
void add(int x,int y){e[++tot].too=y;e[tot].pre=last[x];last[x]=tot;}
void dfs(int x)
{
    dfn[x]=cnt++;
    for(int i=last[x];i;i=e[i].pre)dfs(e[i].too);
    nxt[dfn[x]]=cnt;
}
int main()
{
    read(k);read(n);
    for(int i=1;i<=n;i++)read(s[i]),read(p[i]),read(x),add(x,i),mx=max(mx,p[i]);
    dfs(0);
    double l=0,r=1e4;
    while(r-l>1e-5)
    {
        double mid=(l+r)/2;
        for(int i=1;i<=n;i++)w[dfn[i]]=1.0*p[i]-mid*s[i];
        for(int i=1;i<=n+1;i++)for(int j=0;j<=k+1;j++)f[i][j]=-inf;
        for(int i=0;i<=n;i++)
        for(int j=0;j<=min(i,k+1);j++)
        {
            if(f[i][j]>f[nxt[i]][j])f[nxt[i]][j]=f[i][j];
            if(f[i][j]+w[i]>f[i+1][j+1])f[i+1][j+1]=f[i][j]+w[i];
        }
        if(f[n+1][k+1]>1e-5)l=mid;else r=mid;
    }
    printf("%.3lf\n",l);
}
View Code

  第二种做法仅能在代价为选取点数的情况下使用,直接在树上做背包,但是在对于每一个子节点做完背包之后才把子节点的size加进父节点,这样的复杂度也是O(N^2)的。

技术分享
 
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
using namespace std;
const int maxn=2510;
double eps=1e-5,inf=1e12;
struct poi{int too,pre;}e[maxn];
int n,k,x,tot,sum;
int p[maxn],s[maxn],size[maxn],last[maxn];
double f[maxn][maxn],w[maxn],g[maxn];
void read(int &k)
{
    int f=1;k=0;char c=getchar();
    while(c<0||c>9)c==-&&(f=-1),c=getchar();
    while(c<=9&&c>=0)k=k*10+c-0,c=getchar();
    k*=f;
}
void add(int x,int y){e[++tot].too=y;e[tot].pre=last[x];last[x]=tot;}
void dfs(int x)
{
    size[x]=1;f[x][1]=w[x];
    for(int i=last[x];i;i=e[i].pre)
    {
        dfs(e[i].too);
        for(int j=0;j<=size[x]+size[e[i].too];j++)g[j]=f[x][j];
        for(int j=1;j<=size[x];j++)
        for(int k=1;k<=size[e[i].too];k++)
        g[j+k]=max(g[j+k],f[x][j]+f[e[i].too][k]); 
        size[x]+=size[e[i].too];
        for(int j=1;j<=size[x];j++)f[x][j]=g[j];
    }
}   
int main()
{
    read(k);read(n);
    for(int i=1;i<=n;i++)read(s[i]),read(p[i]),read(x),add(x,i);
    double l=0,r=1e4;
    while(r-l>eps)
    {
        double mid=(l+r)/2;
        for(int i=1;i<=n;i++)w[i]=1.0*p[i]-mid*s[i];
        for(int i=0;i<=n;i++)for(int j=1;j<=k+1;j++)f[i][j]=-inf;
        dfs(0);if(f[0][k+1]>eps)l=mid;else r=mid;
    }
    printf("%.3lf\n",l);
}
View Code

 

bzoj4753: [Jsoi2016]最佳团体(分数规划+树形依赖背包)

标签:1.0   one   答案   ==   hid   iostream   alt   line   ring   

原文地址:http://www.cnblogs.com/Sakits/p/7690778.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!