zoukankan      html  css  js  c++  java
  • 二分---组合数,上界函数的使用

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





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

    today lazy . tomorrow die .
  • 相关阅读:
    Win7下用IIS发布网站
    进程的端口被占用的解决方案
    JS event loop
    慕课网
    angular js
    PowerDesign生成数据库
    CodeSmith 代码生成器
    微信小程序开发学习资料
    SSO 单点登录
    面试
  • 原文地址:https://www.cnblogs.com/france/p/4808682.html
Copyright © 2011-2022 走看看