nyoj1091 (wa 的快进来!!)



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:b
using 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;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值