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; }
版权声明:本文为博主原创文章,未经博主允许不得转载。