1. 程式人生 > >poj3977 - subset - the second time - 暴力 + 二分

poj3977 - subset - the second time - 暴力 + 二分

多次 style 不能 數據類型 二次 color 部分 class 是否

2017-08-26 11:38:42

writer:pprp

已經是第二次寫這個題了,但是還是出了很多毛病

先給出AC代碼:

解題思路:

之前在培訓的時候只是籠統的講了講怎麽做,進行二分對其中一邊進行暴力枚舉,對另一邊用lower_bound查找算出的相反數

現在給出詳細一點的思路:

答案可能在左邊枚舉的部分,也可能在右邊枚舉的部分,也可能在兩邊加起來的和中

所以從這三個方面不能少考慮;

還有用到了map所以算出來的key是唯一的,所以當算出來兩個key相等的時候,應該采用value也就是cnt比較小的那個數

細節比較多,很容易就wa

/*
@prama: poj 3977
@writer:pprp
@declare:暴力+二分
@date:2017/8/26
*/ #include <iostream> #include <cstring> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> #include <map> #define ll long long #define IOS ios::sync_with_stdio(false),cin.tie(0); #define tag cout <<"-------" << endl; using
namespace std; int N; const int maxn = 40; ll subset[maxn]; //改成ll ll ll_abs(ll a) { return a > 0 ? a:- a; } int main() { IOS; while(cin >> N && N) { memset(subset,0,sizeof(subset)); for(int i = 0 ; i < N ; i++) cin >> subset[i];
//初始化這個pair,用來記錄答案 pair<ll,int> ans(ll_abs(subset[0]),1); map<ll,int> mp;// sum -> cnt //二進制枚舉前一半 for(int i = 0 ; i < (1 << N/2) ; i++) { ll sum = 0; int cnt = 0; for(int j = 0 ; j < N/2 ; j++) { //二進制枚舉,判斷第j為是否被選中 if((i >> j)&1) { sum += subset[j]; cnt++; } } //**增加一個判斷,如果一個都沒有被選中 if(cnt == 0) continue; //對結果進行操作,找到當前最小的sum ans = min(ans, make_pair(ll_abs(sum),cnt)); //將每次枚舉的情況都加入map中去 map<ll,int>::iterator ii = mp.find(sum); if(ii != mp.end())//如果能找到,就采用那個比較小的cnt,因為map的key是unique的所以要進行如下判斷 ii->second = min(ii->second,cnt); else //如果沒有找到就直接放到map中去 mp[sum] = cnt; } //對後一半進行枚舉,得到的sum可從map中尋找最接近-sum的值 for(int i = 0 ; i < (1 << (N - N/2)); i++) { ll sum = 0; int cnt = 0; for(int j = 0 ; j < (N - N/2); j++) { if((i >> j) & 1) { cnt++; sum += subset[N/2 +j]; } } //**增加一個判斷,如果一個都沒有被選中 if(cnt == 0) continue; //對結果進行操作,找到當前最小的sum ans = min(ans, make_pair(ll_abs(sum),cnt)); //對後半段進行找最接近於-sum的值 //運用lower_bound查找的是不小於-sum的值,就是比-sum略大的值 map<ll,int>::iterator it = mp.lower_bound(-sum); if(it != mp.end())//如果可以找到就進行比較 { ans = min(ans, make_pair(ll_abs(it->first + sum),it->second+cnt)); } // 有可能是比-sum略小的值,這個也可以用upper_bound來寫 ?? if(it != mp.begin()) { it--; ans = min(ans, make_pair(ll_abs(it->first + sum),it->second+cnt)); } //經過測試,不可以采用這個方法 map<ll,int>::iterator tt = mp.upper_bound(-sum); { ans = min(ans, make_pair(ll_abs(it->first + sum),it->second+cnt)); } } cout << ans.first << " " << ans.second << endl; } return 0; }

遇到的問題:

1、數據類型的選擇,數據範圍是10^15 遠遠超過int類型了,所以一開始沒有檢查數組類型,導致wa了很多次,

也浪費了很長時間。

2、lower_bound和upper_bound的用法還是不是很清楚。

最後那部分不可以用upper_bound直接將指針進行移動就可以

poj3977 - subset - the second time - 暴力 + 二分