/*
translation:
给出一列数列,找出其中的非空连续子序列,使得其和的绝对值最小。如果有相同的和的情况下输出元素个数最少的那个
solution:
折半枚举即可
note:
#: 思路很简单,分成两半,折半枚举即可,但是代码中有很多坑。首先必须对前后两部分只选一个的情况单独考虑。然后如果
ans_sum的预设值为INF的话还是会WA,所以最好设置为随便一个集合的和就行。
date:
2016.11.12
*/
#include <iostream>
#include <cstdio>
#include <map>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 36;
const int INF = 1e30;
typedef long long ll;
map<ll, ll> sum_set; //first:sum second:numbers of elements
ll a[maxn]; int n;
ll abs1(ll x)
{
return x < 0 ? -x : x;
}
int main()
{
//freopen("in.txt", "r", stdin);
while(cin >> n && n){
sum_set.clear();
for(int i = 0; i < n; i++) cin >> a[i];
int fn = n / 2;
int bn = n - fn;
ll ans_sum = a[0], ans_cnt = 1;
for(int i = 0; i < (1 << fn); i++){ //预处理前半部分
ll num = 0, sum = 0;
for(int j = 0; j < fn; j++){
if(i >> j & 1){
num++;
sum += a[j];
}
}
if(num == 0) continue;
if(abs1(ans_sum) > abs1(sum) || (abs1(ans_sum) == abs1(sum) && ans_cnt > num)){
ans_sum = sum;
ans_cnt = num;
}
map<ll, ll>::iterator it = sum_set.find(sum);
if(it != sum_set.end()){
it->second = min(it->second, num);
}else{
sum_set[sum] = num;
}
}
//枚举后半部分
for(int i = 0; i < (1 << bn); i++){
ll num = 0, sum = 0;
for(int j = 0; j < bn; j++){
if(i >> j & 1){
num++;
sum += a[j+fn];
}
}
if(num == 0) continue;
if(abs1(ans_sum) > abs1(sum) || (abs1(ans_sum) == abs1(sum) && ans_cnt > num)){
ans_sum = sum;
ans_cnt = num;
}
//在已经预处理过的前半部分寻找最为接近sum的值
map<ll, ll>::iterator it = sum_set.lower_bound(-sum);
if(it != sum_set.end()){
if(abs1(ans_sum) > abs1(it->first + sum) ||
(abs1(ans_sum) == abs1(it->first + sum) && ans_cnt > it->second + num)){
ans_sum = abs1(it->first + sum);
ans_cnt = it->second + num;
}
}
if(it != sum_set.begin()){
it--;
if(abs1(ans_sum) > abs1(it->first + sum) ||
(abs1(ans_sum) == abs1(it->first + sum) && ans_cnt > it->second + num)){
ans_sum = abs1(it->first + sum);
ans_cnt = it->second + num;
}
}
}
cout << abs1(ans_sum) << " " << ans_cnt << endl;
}
}
poj3977(折半枚举,多坑)
最新推荐文章于 2019-08-22 21:03:00 发布