2019牛客暑期多校训练营(第九场)D Knapsack Cryptosystem(折半搜索)

31 篇文章 0 订阅
16 篇文章 0 订阅

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 &lt; s &lt; 9 × 1 0 18 0&lt;s &lt; 9\times 10^{18} 0<s<9×1018,因此普通背包做不了了。但是注意到 1 ≤ n ≤ 36 1 \leq n \leq 36 1n36,可以想到用 d f s dfs dfs,但复杂度 2 36 2^{36} 236也不太行。

再思考一下,如果这个n可以小点的话,就可以做了。因此我们可以想到用折半搜索,先预处理出前一半的子集,然后排序,再枚举另一半子集,对于另一半的每一个子集和 u u u,去预处理好并排序好的前半部分二分搜索 s − u s-u su即可。

代码

#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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值