2019牛客暑期多校訓練營(第九場)D Knapsack Cryptosystem(折半搜索)

Knapsack Cryptosystem

題意:給出一個序列{ai}\{a_i\}和一個指定的子集和ss,輸出子集(用01表示)。
題解:問題就在於aia_i的值最大有2×10172\times 10^{17}0<s<9×10180<s < 9\times 10^{18},因此普通揹包做不了了。但是注意到1n361 \leq n \leq 36,可以想到用dfsdfs,但複雜度2362^{36}也不太行。

再思考一下,如果這個n可以小點的話,就可以做了。因此我們可以想到用折半搜索,先預處理出前一半的子集,然後排序,再枚舉另一半子集,對於另一半的每一個子集和uu,去預處理好並排序好的前半部分二分搜索sus-u即可。

代碼

#include<bits/stdc++.h>
using namespace std;
#ifndef ONLINE_JUDGE
#define dbg(args...)                                   \
    do{                                                \
	        cout << "\033[32;1m" << #args << " -> ";   \
         err(args);                                    \
      } while(0)                                       
#else
#define dbg(...)
#endif
void err()
{
    cout << "\033[39;0m" << endl;
}
template <template <typename...> class T, typename t, typename... Args>
void err(T<t> a, Args... args)
{
    for (auto x : a) cout << x << ' ';
    err(args...);
}
template <typename T, typename... Args>
void err(T a, Args... args)
{
    cout << a << ' ';
    err(args...);
}
/****************************************************************************************************/
typedef long long LL;
const int N = 40;
LL a[N],ret, ans = 0, n;
map<LL,LL> dp;
int main() {
#ifndef ONLINE_JUDGE
    freopen("input.in","r",stdin);
#endif
    ios::sync_with_stdio(false); cin.tie(0);
	cin >> n >> ret;
	for(LL i = 0; i < n; ++i) {
		cin >> a[i];
	}
	vector<LL> u,v;
	for(LL i = 0; i < (1 << (n / 2)); ++i) {
		pair<LL,LL> p = make_pair(0,0);
		for(LL j = 0; j < n / 2; ++j) {
			if(i & (1 << j)) {
				p.first |= 1 << j;
				p.second += a[j];
			}
		}
		dp[p.second] = p.first;
		u.emplace_back(p.second);
	}
	sort(u.begin(),u.end());
	for(LL i = 0; i < (1 << (n - n / 2)); ++i) {
		pair<LL,LL> p = make_pair(0,0);
		for(LL j = 0; j < (n - n / 2); ++j) {
			if(i & (1 << j)) {
				p.first |= 1 << j;
				p.second += a[j + n / 2];
			}
		}
		if(binary_search(u.begin(),u.end(),ret - p.second)) {
			LL res = (*lower_bound(u.begin(),u.end(),ret - p.second));
			LL S1 = dp[res];
			for(LL j = 0; j < n / 2; ++j) {
				cout << ((S1 >> j) & 1);
			}
			for(LL j = 0; j < n - n / 2; ++j) {
				cout << ((p.first >> j) & 1);
			}
			break;
		}
	}
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章