Knapsack Cryptosystem
题意:给出一个序列
{
a
i
}
\{a_i\}
{ai}和一个指定的子集和
s
s
s,输出子集(用01表示)。
题解:问题就在于
a
i
a_i
ai的值最大有
2
×
1
0
17
2\times 10^{17}
2×1017,
0
<
s
<
9
×
1
0
18
0<s < 9\times 10^{18}
0<s<9×1018,因此普通背包做不了了。但是注意到
1
≤
n
≤
36
1 \leq n \leq 36
1≤n≤36,可以想到用
d
f
s
dfs
dfs,但复杂度
2
36
2^{36}
236也不太行。
再思考一下,如果这个n可以小点的话,就可以做了。因此我们可以想到用折半搜索,先预处理出前一半的子集,然后排序,再枚举另一半子集,对于另一半的每一个子集和 u u u,去预处理好并排序好的前半部分二分搜索 s − u s-u s−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;
}