france

https://github.com/francecil

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

H - LIGHTOJ 1127
Time Limit:2000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu

Description

Given n integers and a knapsack of weight W, you have to count the number of combinations for which you can add the items in the knapsack without overflowing the weight.

Input

Input starts with an integer T (≤ 100), denoting the number of test cases.

Each case contains two integers n (1 ≤ n ≤ 30) and W (1 ≤ W ≤ 2 * 109) and the next line will contain n integers separated by spaces. The integers will be non negative and less than 109.

Output

For each set of input, print the case number and the number of possible combinations.

Sample Input

3

1 1

1

1 1

2

3 10

1 2 4

Sample Output

Case 1: 2

Case 2: 1

Case 3: 8




题目大意:找组合数(比如1 3 5  能组成 0 1 3 5 4 6 8 9(取i个元素的和) )

看是否有几个能不超过W的;普通算出全部(1<<30肯定TLE)  分成两部分,在第二部分找满足还能被第一部分加的


这题不错,学到挺多的(= =好吧其实是我太菜了)

1:组合数怎么求?

利用二进制的思想,n个数那么有1<<n  个组合数  处理的时候从0开始比较好   

比如n=3;组合数有 1<<3 ==8种  

分别是:

000

001

010

011

100

101

110

111

有没有发现一个特点,这正好和 每个元素取或不取是相联系的,0 表示不取,1表示取,那么000表示三个都不取,101表示取第一个和第三个,以此类推

那怎么加,

if(i&(1<<j))      //利用  &的性质  比如 0101  只有0100(4(1<<2)) 0001(1(1<<0))可以满足
					a[i]+=t[j];
所以就是:
	for(int i=0;i<l;i++){     //利用二进制的性质, 比如4个人 就有0-15种 0->0000、15->  1111
			for(int j=0;j<ta;j++){   //1 0 就表示取或不取
				if(i&(1<<j))      //利用  &的性质  比如 0101  只有0100(4(1<<2)) 0001(1(1<<0))可以满足
					a[i]+=t[j];
			}
		}


找第二部分的最大满足的那一项:

先排序,

		sort(b,b+r);
//		lower_bound( a,a+n,key ): 返回一个地址,指向键值>= key的第一个元素。
//		 upper_bound(  a,a+n,key  ):返回一个地址,指向键值> key的第一个元素。
//要得到第几个还得减去数组首地址
//		int a[]={1,2,3,4};
//		cout<<upper_bound(a,a+4,3)-a<<endl;  //output:3
//
//		cout<<lower_bound(a,a+4,3)-a<<endl;//output:2
		for(int i=0;i<l;i++){
			if(w-a[i]>=0){   //=号要有 右边是0的情况
				ans+=upper_bound(b,b+r,w-a[i])-b;
			}
		}

还有要注意的就是引入头文件

#include<iostream>
#include <algorithm>//upper_bound必须包含的头文件
#include<string.h>//memset必须包含的头文件
using namespace std;
#define ll long long
#define maxn 100000
ll a[maxn];
ll b[maxn];
ll t[maxn];
int main(){
	int T;

	cin>>T;
	for(int tt=1;tt<=T;tt++){
		memset(t,0,sizeof(t));
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		int n;
		ll w;
		cin>>n>>w;
		int ta=n>>1;
		int tb=n-ta;
		int l=1<<ta;
		int r=1<<tb;
		for(int i=0;i<n;i++)
			cin>>t[i];

		for(int i=0;i<l;i++){     //利用二进制的性质, 比如4个人 就有0-15种 0->0000、15->  1111
			for(int j=0;j<ta;j++){   //1 0 就表示取或不取
				if(i&(1<<j))      //利用  &的性质  比如 0101  只有0100(4(1<<2)) 0001(1(1<<0))可以满足
					a[i]+=t[j];
			}
		}
		for(int i=0;i<r;i++){  //下面同理
			for(int j=0;j<tb;j++){
				if(i&(1<<j))
					b[i]+=t[ta+j];
			}
		}
		ll ans=0;
		sort(b,b+r);
//		lower_bound( a,a+n,key ): 返回一个地址,指向键值>= key的第一个元素。
//		 upper_bound(  a,a+n,key  ):返回一个地址,指向键值> key的第一个元素。
//要得到第几个还得减去数组首地址
//		int a[]={1,2,3,4};
//		cout<<upper_bound(a,a+4,3)-a<<endl;  //output:3
//
//		cout<<lower_bound(a,a+4,3)-a<<endl;//output:2
		for(int i=0;i<l;i++){
			if(w-a[i]>=0){   //=号要有 右边是0的情况
				ans+=upper_bound(b,b+r,w-a[i])-b;
			}
		}
		cout<<"Case "<<tt<<": "<<ans<<endl;
	}
	return 0;
}





版权声明:本文为博主原创文章,未经博主允许不得转载。

posted on 2014-08-06 09:46  france  阅读(424)  评论(0编辑  收藏  举报