poj3977 Subset(折半列舉)

2021-06-29 00:33:35 字數 1556 閱讀 4258

給定n個整數組成的數列(n<=35),從中選出乙個子集,使得這個子集的所有元素的值的和的絕對值最小,如果有多組資料滿足的話,選擇子集元素最少的那個。

如果單純的列舉的話,這n個數分別有選和不選兩種,所以一共有2^35個子集。很明顯,會超時,但是我們可以將這個整數數列分成兩半,可得每邊最多18個,如果分別進行列舉的話,複雜度可以降到o((n/2)²),即最多2^18,在可接受範圍內。然後列舉其中乙個子集,二分查詢與他組成絕對值最小的子集。在這裡我們使用了stl::map這個類庫,裡面封裝了紅黑樹,所以複雜度可以繼續降低。

列舉的話採用二進位制的列舉方法,這一題也讓我練習了一下。

程式設計的時候遇到了幾個問題,導致各種wa,re和ce以後注意:

1.poj的編譯器好像不能識別__int64的abs,所以這個abs要自己寫,否則會ce;

2.map這個類裡面,第乙個鍵值的值只能有乙個,多了會被覆蓋,所以在新增的時候要特判,因為不熟悉map,因為這個wa了好幾次。

3.空集的情況要單獨判斷,這一點wa了我好久。

下面上**:

#include #include #include #include #include #define max_n 1e16;

using namespace std;

map <__int64,int> sum;

__int64 data[100];

pair<__int64,int>result(1e16,1);

int n;

__int64 ll_abs(__int64 a)

int main()

for(int i=1;i<1<<(n/2);i++)

}map<__int64,int>::iterator it;

it=sum.find(sums);

if(sumn!=0)

else

if(result.first==ll_abs(sums))}}

if(it!=sum.end())

else

sum[sums]=sumn;

}for(int i=1;i<1<<(n-n/2);i++)

}map<__int64,int>::iterator it;

it=sum.lower_bound(-sums);

if(sumn!=0)

else

if(result.first==ll_abs(sums))}}

if(sumn+it->second==0)

continue;

int flag;

for(flag=0;flag<2;it--,flag++)

if(result.first>ll_abs(it->first+sums))

else

if(result.first==ll_abs(it->first+sums))

}if(it==sum.begin())

break;}}

printf("%i64d %d\n",result.first,result.second);

}return 0;

}

POJ3977 Subset 折半列舉

題目大意是給定n個數的集合,從這個集合中找到乙個非空子集,使得該子集元素和的絕對值最小。假設有多個答案,輸出元素個數最少的那個。n最多為35,假設直接列舉顯然是不行的。可是假設我們將這些數分成兩半後再列舉的話,最多有2 18 262144 此時我們兩半列舉後的結果進行排序後再二分搜尋一下就能夠了。複...

POJ 3977 Subset 折半搜尋

題目 給出乙個整數集合,求出非空子集中元素和絕對值最小是多少 元素個數盡量少 題解 分成兩半 爆搜每一半,用map維護前一半的值 每搜出後一半的乙個值就去map裡找和他和絕對值最小的更新答案 include include include include includetypedef long lo...

POJ 3977 Subset 簡單折半列舉

題意 n個數選若干個,使他們和的絕對值最小,如果存在多個解,選擇所選的個數最少的。題解 n為35,列舉所有情況肯定超時,可以列舉一半然後二分找另一半。include include include include include include using namespace std typedef...