题意:在一个集合中找到一个非空子集使得这个子集元素和的绝对值尽量小,和绝对值相同时保证元素个数尽量小
分析:1.二分枚举的思想,先分成两个集合;
2.枚举其中一个集合中所有的子集并且存到数组中,并排序;
3.枚举另一个集合中所有的子集并且与第一个集合中的合适子集相加(可以通过二分查找在数组中找到最合
适的元素)
4.这个题特别坑的地方是不能用abs库函数,只能手写
AC代码:
#include<stdio.h> #include<map> #include<algorithm> using namespace std; #define ll long long ll a[50]; ll Abs(ll x) { return x<0?-x:x; } int main( ) { int n; while(scanf("%d",&n)!=EOF) { if(n==0) break; for(int i=0 ; i<n ; i++) scanf("%lld",&a[i]); int n2=n/2; pair<ll,int> ans(Abs(a[0]),1); map<ll,int>M; map<ll,int>::iterator it; for(int i=1 ; i<1<<n2 ; i++) { ll sum=0;int cnt=0; for(int j=0 ; j<n2 ; j++) { if(i>>j&1) { sum+=a[j]; cnt++; } } ans = min(ans,make_pair(Abs(sum),cnt)); if(M[sum]) M[sum]=min(M[sum],cnt); else M[sum]=cnt; } for(int i=1 ; i<1<<(n-n2) ; i++) { ll sum=0;int cnt=0; for(int j=0 ; j<n-n2 ; j++) { if(i>>j&1) { sum+=a[j+n2]; cnt++; } } ans=min(ans,make_pair(Abs(sum),cnt)); it = M.lower_bound(-sum); if(it != M.end()) ans = min(ans,make_pair(Abs(sum+it->first),cnt+it->second)); if(it != M.begin()) { it--; ans = min(ans,make_pair(Abs(sum+it->first),cnt+it->second)); } } printf("%lld %d ",ans.first,ans.second); } return 0; }
带解析
#include <iostream> #include <algorithm> #include <limits> #include <map> using namespace std; typedef long long LL; #define MAX_N 36 LL number[MAX_N]; LL ll_abs(const LL& x) // damn it! error C3861: 'llabs': identifier not found { return x >= 0 ? x : -x; } ///////////////////////////SubMain////////////////////////////////// int main(int argc, char *argv[]) { #ifndef ONLINE_JUDGE freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif int N; while (cin >> N && N) { for (int i = 0; i < N; ++i) { cin >> number[i]; } map<LL, int> dp; // sum的值<->集合元素个数,这里不是绝对值 pair<LL, int> result(ll_abs(number[0]), 1); // 最优解 for (int i = 0; i < 1 << (N / 2); ++i) // 枚举前 N / 2 { LL sum = 0; int num = 0; for (int j = 0; j < N / 2; ++j) { if ((i >> j) & 1) { sum += number[j]; ++num; } } if (num == 0) { continue; } result = min(result, make_pair(ll_abs(sum), num)); map<LL, int>::iterator it = dp.find(sum); if (it != dp.end()) { it->second = min(it->second, num); } else { dp[sum] = num; } } for (int i = 0; i < 1 << (N - N / 2); ++i) // 枚举剩下的 { LL sum = 0; int num = 0; for (int j = 0; j < N - N / 2; ++j) { if ((i >> j) & 1) { sum += number[N / 2 + j]; ++num; } } if (num == 0) { continue; } result = min(result, make_pair(ll_abs(sum), num)); // 找寻跟-sum最相近的结果 map<LL, int>::iterator it = dp.lower_bound(-sum); // 返回大于或等于-sum的第一个元素位置 if (it != dp.end()) {// 可能是该位置 result = min(result, make_pair(ll_abs(sum + it->first), num + it->second)); } if (it != dp.begin()) {// 或比该元素小一点点的 --it; result = min(result, make_pair(ll_abs(sum + it->first), num + it->second)); } } cout << ll_abs(result.first) << ' ' << result.second << endl; } #ifndef ONLINE_JUDGE fclose(stdin); fclose(stdout); system("out.txt"); #endif return 0; }