zoukankan      html  css  js  c++  java
  • poj_3977 折半枚举

    题目大意

        给定N(N<=35)个数字,每个数字都<= 2^15. 其中一个或多个数字加和可以得到s,求出s的绝对值的最小值,并给出当s取绝对值最小值时,需要加和的数字的个数。

    题目分析

        需要枚举集合的所有情况,2^35,会超时。考虑使用折半枚举的方法,考虑前 N/2个数字构成的集合S1,在S1中进行所有情况枚举,复杂度为 2^17,并将所有可能的和sum以及构成和sum需要的数字个数count存放在map M中;然后在S2中进行所有情况的枚举,复杂度为2^17,对于每种情况的sum2,在M中查找 -sum2的位置,在该位置前后位置处进行查找,求和的最小值。 
        还需要考虑,当s只有S1中的数字构成或者s只有S2中的数字构成,或者s由S1和S2中的数字构成的三类情况。 
        总的时间复杂度为 O(2^17 + 2^17*log(2^17)) = O(2^22)

    实现(c++)

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<string>
    #include<cmath>
    #include<iostream>
    #include<map>
    using namespace std;
    
    long long int ll_abs(long long int n){
    	if (n >= 0)
    		return n;
    	return -n;
    }
    long long int an[40];
    
    map<long long int, int> sum_map;
    int main2(){
    	int n;
    	while (scanf("%d", &n) && n){
    		map<long long int, int>::iterator it;
    		
    
    		sum_map.clear();
    		for (int i = 0; i < n; i++){
    			scanf("%lld", &an[i]);
    		}
    		long long int min_sum = ll_abs(an[0]);
    		int min_count = 1;
    
    		int m = n / 2;
    		for (int i = 0; m > 0 && i < (1 << m); i++){
    			long long int sum = 0;
    			int count = 0;
    			int t = i;
    			for (int k = 0; k < m; k++){
    				if (t & 1){
    					sum += an[k];
    					count++;
    				}
    				t >>= 1;
    			}
    			if (count == 0)
    				continue;
    
    			if (sum_map.find(sum) != sum_map.end()){
    				sum_map[sum] = min(sum_map[sum], count);
    			}
    			else
    				sum_map[sum] = count;
    			if (ll_abs(sum) < min_sum){
    				min_sum = ll_abs(sum);
    				min_count = count;
    			}
    			else if (ll_abs(sum) == min_sum){
    				min_count = min(min_count, count);
    			}
    		}
    		m = n / 2 + n % 2;
    		for (int i = 0; i < (1 << m); i++){
    			long long int sum = 0;
    			int count = 0;
    			int t = i;
    			for (int k = 0; k < m; k++){
    				if (t & 1){
    					sum += an[n / 2 + k];
    					count++;
    				}
    				t >>= 1;
    			}
    			if (count == 0)
    				continue;
    
    
    			if (ll_abs(sum) < min_sum){
    				min_sum = ll_abs(sum);
    				min_count = count;
    			}
    			else if (ll_abs(sum) == min_sum){
    				min_count = min(min_count, count);
    			}
    
    			it = sum_map.lower_bound(-sum);
    			if (it != sum_map.end()){
    				long long int s = sum + it->first;
    				if (ll_abs(s) < min_sum){
    					min_sum = ll_abs(s);
    					min_count = it->second + count;
    				}
    				else if (ll_abs(s) == min_sum){
    					min_count = min(min_count, it->second + count);
    				}
    			}
    			if (it != sum_map.begin()){
    				--it;
    				long long int s = sum + it->first;
    				if (ll_abs(s) < min_sum){
    					min_sum = ll_abs(s);
    					min_count = it->second + count;
    				}
    				else if (ll_abs(s) == min_sum){
    					min_count = min(min_count, it->second + count);
    				}
    			}
    		}
    
    		printf("%lld %d
    ", min_sum, min_count);
    	}
    	return 0;
    }
    
  • 相关阅读:
    According to TLD or attribute directive in tag file, attribute end does not accept any expressions
    Several ports (8080, 8009) required by Tomcat v6.0 Server at localhost are already in use.
    sql注入漏洞
    Servlet—简单的管理系统
    ServletContext与网站计数器
    VS2010+ICE3.5运行官方demo报错----std::bad_alloc
    java 使用相对路径读取文件
    shell编程 if 注意事项
    Ubuntu12.04下eclipse提示框黑色背景色的修改方法
    解决Ubuntu环境变量错误导致无法正常登录
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4909448.html
Copyright © 2011-2022 走看看