• [Jsoi2016]最佳团体 BZOJ4753 01分数规划+树形背包/dfs序


    分析:

    化简一下我们可以发现,suma*ans=sumb,那么我们考虑二分ans,之后做树形背包上做剪枝。

    时间复杂度证明,By GXZlegend O(nklogans)

    附上代码:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstdlib>
    #include <cstring>
    #include <queue>
    #include <iostream>
    using namespace std;
    #define N 2505
    #define eps 5e-4
    #define max(a,b) ((a)<(b)?(b):(a))
    double f[N][N],ans;
    int a[N],b[N],dep[N],head[N],cnt,k,n,siz[N];
    struct node
    {
    	int to,next;
    }e[N<<1];
    void add(int x,int y)
    {
    	e[cnt].to=y;
    	e[cnt].next=head[x];
    	head[x]=cnt++;
    	return ;
    }
    void dfs(int x,int from)
    {
    	dep[x]=dep[from]+1;siz[x]=1;
    	if(x)f[x][1]=-ans*a[x]+b[x];
    	for(int i=head[x];i!=-1;i=e[i].next)
    	{
    		int to1=e[i].to;
    		if(to1!=from)
    		{
    			dfs(to1,x);
    			int o=min(k,siz[x]+siz[to1]),u=min(k,siz[to1]);
    			for(int j=o;j>=1;j--)
    			{
    				for(int l=max(j-siz[x],1);l<=u;l++)
    				{
    					if(l>j)break;
    					f[x][j]=max(f[x][j],f[x][j-l]+f[to1][l]);
    				}
    			}
    			siz[x]+=siz[to1];
    		}
    	}
    }
    bool check(double x)
    {
    	for(int i=0;i<=n;i++)for(int j=0;j<=k;j++)f[i][j]=-1e9;
    	dep[0]=f[0][0]=0;
    	ans=x;
    	dfs(0,0);
    	if(f[0][k]>0)return 1;
    	return 0;
    }
    int main()
    {
    	// freopen("sales.in","r",stdin);
    	// freopen("sales.out","w",stdout);
    	memset(head,-1,sizeof(head));
    	scanf("%d%d",&k,&n);
    	for(int i=1;i<=n;i++)
    	{
    		int x;
    		scanf("%d%d%d",&a[i],&b[i],&x);
    		add(x,i);
    	}
    	double l=0,r=10000;
    	while(l<r-eps)
    	{
    		double mid=(l+r)/2;
    		if(check(mid))l=mid;
    		else r=mid;
    	}
    	printf("%.3lf
    ",l);
    	return 0;
    }
    

    其实这种方法就能跑的飞起,虽然是递归的,并且状态和转移比较多。

    其实还有别的方法,比如说将它转化为dfs序上做背包。

    我们知道,选择一个必须包含根的联通块,我们就可以这样考虑,状态和上面的差不多,f[i][j]表示dfs序上选择第i个点,在i-n中选j个的最大答案

    转移:f[i][j]=f[i+1][j-1]+x*a[idx[i]]+b[idx[i]]和f[i][j]=max(f[i][j],f[i+siz[idx[i]][j]);

    最后判断f[1][k]是否大于0

    附上代码:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstdlib>
    #include <cstring>
    #include <queue>
    #include <iostream>
    using namespace std;
    #define N 2505
    #define eps 5e-4
    #define max(a,b) ((a)<(b)?(b):(a))
    double f[N][N],ans;
    int a[N],b[N],head[N],cnt,k,n,idx[N],x,siz[N],tims;
    struct node
    {
    	int to,next;
    }e[N<<1];
    void add(int x,int y)
    {
    	e[cnt].to=y;
    	e[cnt].next=head[x];
    	head[x]=cnt++;
    	return ;
    }
    void dfs(int x,int from)
    {
    	idx[++tims]=x;siz[x]=1;
    	for(int i=head[x];i!=-1;i=e[i].next)
    	{
    		int to1=e[i].to;
    		if(to1!=from)
    		{
    			dfs(to1,x);
    			siz[x]+=siz[to1];
    		}
    	}
    }
    bool check(double x)
    {
    	for(int i=1;i<=n+2;i++)
    	{
    		for(int j=0;j<=k;j++)
    		{
    			f[i][j]=-1e9;
    		}
    	}
    	f[n+2][0]=0;
    	for(int i=n+1;i>=1;i--)
    	{
    		int t=idx[i];
    		int u=min(k,n+2-i);
    		if(i!=1)
    		{
    			for(int j=u;j>=1;j--)
    			{
    				f[i][j]=f[i+1][j-1]-x*a[t]+b[t];
    			}
    		}else
    		{
    			for(int j=k;j>=1;j--)
    			{
    				f[i][j]=f[i+1][j];
    			}
    		}
    		for(int j=u;j>=0;j--)
    		{
    			f[i][j]=max(f[i][j],f[i+siz[t]][j]);
    		}
    	}
    	if(f[1][k]>0)return 1;
    	return 0;
    }
    int main()
    {
    	// freopen("sales.in","r",stdin);
    	// freopen("sales.out","w",stdout);
    	memset(head,-1,sizeof(head));
    	scanf("%d%d",&k,&n);
    	for(int i=1;i<=n;i++)
    	{
    		int x;
    		scanf("%d%d%d",&a[i],&b[i],&x);
    		add(x,i);
    	}
    	dfs(0,0);
    	double l=0,r=10000;
    	while(l<r-eps)
    	{
    		double mid=(l+r)/2;
    		if(check(mid))l=mid;
    		else r=mid;
    	}
    	printf("%.3lf
    ",l);
    	return 0;
    }
    

      

  • 相关阅读:
    Jenkins自动化部署入门详细教程
    单元测试
    弱网测试
    Token、Cookie和Session
    测试开发人员必备Linux命令
    TestNG(一)
    char和varchar
    你平时会看日志吗,一般会出现哪些异常(Exception)
    内存溢出和内存泄漏的区别,产生原因以及解决方案
    测试一个电梯
  • 原文地址:https://www.cnblogs.com/Winniechen/p/9094630.html
Copyright © 2020-2023  润新知