• 题解——gift(dfs序处理树上背包问题)


    题解——gift(dfs序处理树上背包问题)

    本题ssw02参考了蒋神的代码
    模拟赛,蒋神在T1挂了的情况下依然两校区rank1

    欢迎转载ssw02的博客:https://www.cnblogs.com/ssw02/p/11640828.html

    题目

    题面描述: 一个带限制的树上背包问题

    输入:一棵树+各种限制条件(属性)

    输出:价值和消费比的最大值

    思路

    做这道题之前,先把这道题灭了再说[JSOI2016]最佳团体

    先二分答案(01分数规划套路),然后一样处理出 val[ i ] = ( 价值 - mid × 花费 )

    DP的转移方程有点不一样。由于有了人手至少一份和a[ i ]的限制,我们不妨考虑,dp[ i ][ j ]中的 j 值不再是选了 j 种物品,而是有j 个人已经获得了物品。

    然后考虑转移的问题,我们先考虑这种情况:每个人都有了一份礼物,然后现在手中还有一些礼物。我们明显要最优值时,肯定可以将剩下的多余的物品再发给已有物品的人,这样就可以得到剩余的 val > 0 的物品的贡献值。
    所以说 dp[ 一个状态 ][ M ] = dp[ 前一个状态 ][ M ]

    同时我们考虑到,只有至少人手一份,才是一种合法的答案,为了尽可能快的达到人手一份的限制,我们肯定会在分一个物品时优先把这个物品分给没有拿到物品的人,这样可以让我们在选满后有更多的余地选择上述的更加优的物品。

    简单举个例子:如果可以填满然而没有填满,例如还有 5 个人没有物品,但此时你有两件val为 5 , -10 , 个数为 3 4 的物品,你无论如何都无法避免地选到val 为负值的物品,而填满的情况则可以自由选择。(ssw02只能口胡,感性分析正确性)

    然后及时有树形依赖关系背包问题。

    我们可以考虑使用树上背包的惯用做法 dp[ i ][ j ] 处理 i 子树中 j 个人有礼物 。

    也可以使用 dfs序 将属性依赖关系转化到序列上 。采用不选择就跳子树的操作.dp[ i ][ j ]表示处理dfs序 i 后 j 个人有礼物的情况。

    代码

    ssw02参考蒋神代码后选择了常数较小的dfs序DP写法,这种做法倒叙枚举和顺序枚举没有本质区别

    #include<bits/stdc++.h>
    using namespace std ;
    #define ll long long
    const int MAXN = 1005 ;
    inline int read(){
    	int s=0 ; char g=getchar() ; while(g>'9'||g<'0')g=getchar() ;
    	while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s ;
    }
    int N , K ; 
    double p[ MAXN ] , s[ MAXN ] , val[ MAXN ] , dp[ MAXN ][ MAXN ];
    int  tot = 1 , spread[ MAXN ], head[ MAXN ] , to[ MAXN*2 ] , nex[ MAXN*2 ] ;
    int  cnt = 0 , dfn[ MAXN ] , id[ MAXN ], size[ MAXN ] ;
    void  add( int x , int y ){
    	to[ ++tot ] = y , nex[ tot ] = head[ x ] , head[ x ] = tot ;
    }
    inline void  dfs( int u , int fa ){//从0开始只是因为树根为 0 , 刨除贡献 
    	dfn[ u ] = cnt , id[ cnt++ ] = u , size[ u ] = 1 ;
    	for( int i = head[ u ] ; i ; i = nex[ i ] ){
    		dfs( to[ i ] , u  ) ;
    		size[ u ] += size[ to[ i ] ] ;
    	}
    	return ;
    } 
    inline bool check( double x ){
    	for( int i = 1 ; i <= N ; ++i )val[ i ] = p[ i ] - x*s[ i ] ;
    	dp[ N+1 ][ 0 ] = 0 ;
    	for( int i = 1; i <= K ; ++i ) dp[ N+1 ][ i ] = -1e9 ;
    	for( int i = N ; i > 0 ; --i ){
    		int u = id[ i ];
    		for( int j = 0 ; j <= K ; ++j ){
    			dp[ i ][ j ] = dp[ i+size[ u ] ][ j ] ;
    			if(j <= spread[u] ){
    				if(dp[i+1][0]+val[u]>dp[i][j])
    				{
    					dp[i][j]=dp[i+1][0]+val[u];
    				}
    			}
    			else
    			{
    				if(dp[i+1][j-spread[u]]+val[u]>dp[i][j])
    				{
    					dp[i][j]=dp[i+1][j-spread[u]]+val[u];
    				}
    			}
    		}
    	}
    	return dp[ 1 ][ K ] >= 0 ;
    }
    /*错误代码
    for( int i = 1 ; i <= N ; ++i )val[ i ] = p[ id[i] ] - x*s[ id[i] ] ;
    	dp[ 0 ][ 0 ] = 0 ; 
    	for( int j = 0 ; j <= K ; ++j )dp[ 0 ][ j ] = -1e8 ; 
    	for( int i = 0 ; i <= N ; ++i )
    	   for( int j = 0 ; j <= min( i , K ) ; ++j ){
    	   	   if( j <= spread[ id[ i ] ] )
    			  dp[ i+1 ][ j+1 ] = max( dp[ i+1 ][ j+1 ] , dp[ i ][ 0 ]+val[ i ] ) ;
    	   	   else
    	   	   	  dp[ i+1 ][ j+1 ] = max( dp[ i+1 ][ j+1 ] , dp[ i+1 ][ j-spread[ id[i] ]] + val[ i ] ) ;
    		   dp[ i+size[ id[i] ] ][ j ] = max( dp[ i+size[ id[i] ] ][ j ] , dp[ i ][ j ] ) ;
    	   }
    	return dp[ N+1 ][ K ] >= 0 ;
    */
    void  dx( double l , double r ){
    	while( r - l > 0.0000001 ){
    		double mid = ( l+r )/2.0 ;
    		if( check(mid) )l = mid ; else r = mid ; 
    	}
    	printf("%0.10lf",l) ;
    }
    int main(){ 
        //freopen("gift.in","r",stdin);
        //freopen("gift.out","w",stdout);
    	N = read() , K = read() ; int m1 , m2 , m3 ; double ma = -1.0 ;
    	for( register int i = 1 ; i <= N ; ++i ){
    		spread[ i ] = read() , p[ i ] = 1.0*read() , s[ i ] = 1.0*read() , m3 = read() ; 
    		add( m3 , i ) ; 
    	}
    	dfs( 0 , 0 ) ;
    	val[ 0 ] = 0.0 ;
    	dx( (double)0 , 100000 ) ; 
    	return 0 ;
    }
    
    

    如有不足,请各位指出。

  • 相关阅读:
    update condition 字段报错
    Xshell连接Linux服务器总掉线
    sleep php函数
    ubuntu 16.04 镜像下载
    多线程Parallel和Task
    AngularJS 时间格式化
    正则表达式
    手机抓包
    内存泄漏
    字符集编码和排列规则
  • 原文地址:https://www.cnblogs.com/ssw02/p/11640828.html
Copyright © 2020-2023  润新知