2019牛客暑期多校训练营(第九场) D.Knapsack Cryptosystem

D.Knapsack Cryptosystem

题目链接: https://ac.nowcoder.com/acm/contest/889/D

Problem Description

Amy asks Mr. B problem D. Please help Mr. B to solve the following problem.
Amy wants to crack Merkle–Hellman knapsack cryptosystem. Please help it.
Given an array { a i } \{a_i\} {ai} with length n n n, and the sum s s s.
Please find a subset of { a i } \{a_i\} {ai}, such that the sum of the subset is s s s.
For more details about Merkle–Hellman knapsack cryptosystem Please read
https://en.wikipedia.org/wiki/Merkle%E2%80%93Hellman_knapsack_cryptosystem
https://blog.nowcoder.net/n/66ec16042de7421ea87619a72683f807
Because of some reason, you might not be able to open Wikipedia.
Whether you read it or not, this problem is solvable.

Input

The first line contains two integers, which are n ( 1 ≤ n ≤ 36 ) n(1 \leq n \leq 36) n(1n36) and s ( 0 ≤ s &lt; 9 ∗ 1 0 18 ) . s(0 \leq s \lt 9 * 10^{18}). s(0s<91018).
The second line contains n n n integers, which are { a i } ( 0 &lt; a i &lt; 2 ∗ 1 0 17 ) . \{a_i\}(0 \lt a_i \lt 2 * 10^{17} ). {ai}(0<ai<21017).
{ a i } \{a_i\} {ai} is generated like in the Merkle–Hellman knapsack cryptosystem, so there exists a solution and the solution is unique.
Also, according to the algorithm, for any subset sum s s s, if there exists a solution, then the solution is unique.

Output

Output a 01 01 01 sequence.
If the i i i-th digit is 1 1 1, then a i a_i ai is in the subset.
If the i i i-th digit is 0 0 0, then a i a_i ai is not in the subset.

Sample Input

8 1129
295 592 301 14 28 353 120 236

Sample Output

01100001

题意

给定 n , s n,s n,s n n n个数 { a i } ( 1 ≤ i ≤ n ) \{a_i\}(1 \leq i \leq n) {ai}(1in),需要在这 n n n个数中取出若干个数,使得他们的和为 s s s,输出长度为 n n n 01 01 01字符串,为 1 1 1则代表取第 i i i个数。
输入数据保证解唯一。

思路

看到题目就想到了二进制枚举啊,但是 2 32 2^{32} 232有点大,所以一开始否定了这种想法,开始了 d f s dfs dfs疯狂 T L E TLE TLE的试探。
后来 T L E TLE TLE到自闭,神奇的队友突然冒出了一个想法, 2 32 2^{32} 232有点大,那把这 32 32 32拆开再二进制枚举就可以了啊,极限 2 ∗ 2 16 2*2^{16} 2216是可以跑完的。
所以思路就是折半二进制枚举啦。
首先对前 n / 2 n/2 n/2个数进行二进制枚举,用 m a p map map记录前 n / 2 n/2 n/2个数所能够凑出的总和,并映射到该情况的二进制状态。再对后 n − n / 2 n-n/2 nn/2个数进行二进制枚举,凑出的总和此处记为 s u m sum sum,则判断一下 m a p map map中有没有 s − s u m s-sum ssum存在,若有则说明找到了答案,并取出此时前 n / 2 n/2 n/2个数的二进制状态,然后将前后两半部分的二进制状态转换为 01 01 01串即可。

代码

#include <bits/stdc++.h>
#define pi acos(-1.0)
#define ll long long
#define ull unsigned long long
#define esp 1e-9
#define inf 0x3f3f3f3f
#define inff 0x3f3f3f3f3f3f3f3f
#define Pair pair<ll, ll>
#define It list<node>::iterator
   
using namespace std;

const ll N = 40;
bool flag;
ll n, s, a[N], ans[N];
map<ll, ll> mp;

int main(){
	ios::sync_with_stdio(false);
	while (cin>>n>>s){
		mp.clear();//清空map
		flag = false;//这个好像没用,是之前dfs时初始化的标记
		memset(ans, 0, sizeof(ans));//初始化ans(01串)
		for (ll i = 1; i <= n; i++){
			cin>>a[i];
		}
		ll tmp = n/2;
		for (ll i = 0; i < (1LL<<tmp); i++){//前n/2个数二进制枚举
			ll t = i, sum = 0;
			for (ll j = tmp-1; j >= 0; j--){
				if (t & 1){
					sum += a[j+1];//计算和
				}
				t >>= 1;
			}
			mp[sum] = i;//总和 映射到 二进制状态
		}
		tmp = n-tmp;
		for (ll i = 0; i < (1LL<<tmp); i++){//后n-n/2个数二进制枚举
			ll t = i, sum = 0;
			for (ll j = tmp-1; j >= 0; j--){
				if (t & 1){
					sum += a[n-tmp+j+1];//计算和
				}
				t >>= 1;
			}
			if (mp[s-sum]){//前n/2部分的某状态+sum == s(找到答案)
				t = mp[s-sum];
				for (ll j = (n-tmp)-1; j >= 0; j--){//将二进制状态转换为01串
					if (t & 1){
						ans[j+1] = 1;
					}
					t >>= 1;
				}
				t = i, sum = 0;
				for (ll j = tmp-1; j >= 0; j--){//将二进制状态转换为01串
					if (t & 1){
						ans[n-tmp+j+1] = 1;
					}
					t >>= 1;
				}
				break;
			}
		}
		for (ll i = 1; i <= n; i++){//输出01串
			cout<<ans[i];
		}
		cout<<endl;
	}
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值