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;
}