二分---组合数,上界函数的使用

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 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/france/p/4808682.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值