poj 3977 子集
Posted shuaihui
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj 3977 子集相关的知识,希望对你有一定的参考价值。
题意:在一个集合中找到一个非空子集使得这个子集元素和的绝对值尽量小,和绝对值相同时保证元素个数尽量小
分析: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\n",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; }
以上是关于poj 3977 子集的主要内容,如果未能解决你的问题,请参考以下文章