HDU 2639 Bone Collector II DP

首先相信你已经看过《背包九讲》。对于每一次决策后,我们都能得到一组值:F[ i,  j]   I 表示进行了i次决策,j表示占用了j 的体积。最终获得了F[i,j]的收益。这么考虑的话,很显然,就能得到最优子结构的性质

:如果最终能得到Fmax[ n, v ] ,那对于每一组i,j 必定 F[i,j]=Fmax[i,j]。因此,在遍历树的时候,如果两种决策有相同的i,j 那我们可以取出两者中的最大值,另外一种就被无情的抛弃了-_- 。这样,对于每一组的i,j 我们可以方便的按如下方程求出:

F[i,j]=max( F[i-1,j] , f[i-1][j-volume[i]]  + value[i] ) 按照这个方程,就能方便地求出最优解

 

再分析一下这个方程。他符合之前说的两种决策:选取物品i,或不选。最终的结果,是我们能在O(1)的时间内,判定对于体积j,是否应当选取第i件物品。 我们在这里作出了最优的选择。那被我们抛弃的选择呢?他很可能是次优解,第三优解,无论怎样,他都对我们本题求前K优解,起到了重要的作用!

那么,本题的算法,就水到渠成了。设数组[ i , j , p ]。表示对于一组 i,j,  F[i,j,p]  为其第P优的决策。那么,上文的max操作,就需要稍加扩展,变为对两个队列的合并(一种决策的第a1优解很可能是总体的第a2优解)。毋庸置疑,这两个队列是有序的,将其合并即可完成本次决策。代价,则从O(1) 涨到了O(k) ,总的时间复杂度由O(vn) 升至O(vnk) 刚好多了一维。RQNOJ上有前人写好的代码,可读性非常高,我就不贴我自己写的无比粗糙的代码了。

用个形象的比喻吧:如果我想知道学年最高分,那么,我只要知道每个班级的最高分,然后统计一遍就可以了。如果我想知道学年前十呢?我必须要知道每个班的前十名。大家在心里模拟一下,对,这就是本题核心的算法。两种决策,就可以看作这个学年只有两个班。

由于我们在选的时候把次优解已经丢掉了,其实次优解也可以和次优解可以合成次优解(这就是为什么不能丢掉次优解),这就是k循环的一个 的目的,再合并找出前k个最大的,为什么呢?(比喻以说明了)。

#include<stdio.h>

#include<stdlib.h>

void ZeroOnePack( int N,int V, int K, int val[], int volumn[] )

{

    int f[1024][32]={0},A[32]={0},B[32]={0};

    for( int i=1; i<= N; i++ )

    {

        for( int j=V; j>=volumn[i]; j-- )

        {

             int  t=j-volumn[i];

            for( int k=1;k<=K; k++ )//寻找体积j的前k个最优解合成的最优解 

            {

                  A[k]=f[t][k]+val[i];

                  B[k]=f[j][k];     

            }

            int a=1,b=1,c=1;

            while( c<=K&&( a<=K||b<=K ) )//合并重新生成前k个最优解 

            {

                 if( A[a]>B[b] )

                  f[j][c]=A[a],a++;

                  else f[j][c]=B[b],b++;

                  if( f[j][c]!=f[j][c-1] )

                       c++;     

            }     

        }     

    }

    printf( "%d\n",f[V][K] );     

}

int main()

{

    int n,N,V,k,val[124],volumn[124];

    scanf( "%d",&n );

    for( int i=1; i<=n ; i++ )

    {

        scanf( "%d%d%d",&N,&V,&k );

        for( int j=1; j<=N; j++ )

           scanf( "%d",&val[j] );

        for( int j=1; j<=N; j++ )

           scanf( "%d",&volumn[j] );

        ZeroOnePack( N,V,k,val,volumn );     

    }

    return 0;    

}

  

你可能感兴趣的:(Collector)