nyoj1091 (wa 的快进来!!)

来源:互联网 发布:天际原版捏脸数据 编辑:程序博客网 时间:2024/04/20 12:05


nyoj1091


   思路: n=40 ,其他数据太大肯定不能递推,就枚举 分成 前n/2 和 后 n- n/2,后面枚举过程中找到与前面的合适的剩余重量,更新最大价值。

   用到了  二进制的枚举

    000 - 111  的过程就表示了  三个数的任意组合

    001  ---1

    010 ---- 2

    011 ---- 1,2

    100 ---- 3

    101 ----1,3

     ....

    111----1,2,3


    1.  最痛苦的就是,数据类型 long long int  ,数据太大,int 果然不行!

    2. 一定要去掉一些不符合的数据


           比如枚举的前 n/2 ,排序后 ,会存在一些情况

           (w ,v)

           2 4 

           3 2

           这时候 后面枚举过程中与之组合时 ,比如W =  5 ,sw = 2 ,sv =  4

           寻找W-sw 对应的价值时

           组成的最大  是8 而不是6

           这时,需要去掉3 2

 

#include<stdio.h>#include<algorithm>#define max(a,b) a>b?a:busing namespace std;typedef long long int ll;struct value{ll w,v;}S[10000000];int cmp( struct value a ,struct value b){if( a.w < b.w )return 1;else if( a.w == b.w  && a.v < b.v) return 1;return 0;}ll w[45],v[45];int upper(int l ,int h , ll w)   //如果出现 1,3,3,4,5  例如找3 那么返回的是最后的3的下标(由于排序后重量相同的 价值最大的排到了最后){    int mid ;while( l < h ){mid = (l+h)/2;if( w >= S[mid].w)l =  mid + 1;else h = mid;}return l-1;}int main(){    int n1,n2,s,L; ll sw,sv,W,n;int i,j,index;ll ans;while(~scanf("%lld %lld",&n,&W)){        if(! n && ! W) break;for(i = 0;i<n;i++) scanf("%lld %lld",&w[i],&v[i]);n1 = n/2;for(s = 0; s < 1<<n1 ;s++ ){   sw = 0;   sv = 0;           for(j = 0;j < n1;j++)if(s & (1<<j)){    sw += w[j];sv += v[j];}             S[s].w  = sw;   S[s].v = sv;}sort(S , S + s ,cmp);j = 1;    for( i = 1;i< s;i++){if( S[j-1].v < S[i].v){S[j] = S[i];j++;}           }L  = j ;n2 = n-n1;ans = -1;for( s = 0 ;s< (1<<n2);s++){sw = 0;sv = 0;for(j = 0; j<n2;j++)if( s & (1<<j) ){sw += w[j+n1];sv += v[j+n1];}            if( sw <= W ){                index  = upper(0 ,L ,W - sw);               //printf("%d %d %d \n",sw,index ,S[index].v);              if( S[index].w + sw <= W)  ans = max( ans , S[index].v + sv );               }               }printf("%lld\n",ans);}return 0;}




1 0
原创粉丝点击