折半搜索 - 某种密码

某种密码

描述
关于某种密码有如下描述:某种密码的原文A是由N个数字组成,而密文B是一个长度为N的01数串,原文和密文的关联在于一个钥匙码KEY。
若KEY=∑(Ai∗Bi)KEY=∑(Ai∗Bi),则密文就是原文的一组合法密码。
现在有原文和钥匙码,请编一个程序来帮助他统计到底有多少个符合条件的密文。

输入
第一行两个数N,KEY,意义同题目描述;
第二行N个数表示原文A,意义同题目描述。

输出
一个数ANS,表示对于原文A和KEY,有多少组可行的密文B。

样例输入
3 2
1 1 2
样例输出
2

【样例说明】
密文110,1∗1+1∗1+0∗2=21∗1+1∗1+0∗2=2
密文001,0∗1+0∗1+1∗2=20∗1+0∗1+1∗2=2
一共两组可行的密文。

【数据约定】
60%数据满足N<=25
100%数据满足N<=40,-maxlongint<=∑Ai<=maxlongint


Analysis

虽然我没有看出来,但这就是一个0/1背包的变种问题啊
KEY就是背包容积,Ai就是每个物品的容积,求恰好装满背包的方案数。
是不是满简单的??

等等,再看看数据范围,emmm……
这个包好像太大了一点

怎么办呢?既然Ai不行就从 n n n下手
那么小的数据范围,暴力搜索吧
但如果只是暴力搜索的话, 2 40 2^{40} 240貌似行不通
怎么办怎么办?哈…… 2 40 2^{40} 240不行,一半总可以搜出来吧
那我们就折半搞(以后这种数据范围好像可以搜,又好像会挂,就可以试试折半了)
将物品集合均分成两个交集为空,补集为全集的集合A、B,对集合A暴力枚举其所有子集中元素和并存入哈希表(可重集),再对集合B暴力枚举每个子集的元素和s,同时查找哈希表中值为(key−s)的元素个数并计数。时间复杂度为O(2∗2n/2)=O(2n/2),可以接受。


Code
#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define ll long long
using namespace std;
int n,a[50],b[50];
ll key;
tr1::unordered_map<ll,int> M;
void dfs(int pos,int end,ll sum){
	if(pos==end+1){
		M[sum]++;
		return;
	}
	dfs(pos+1,end,sum+a[pos]);
	dfs(pos+1,end,sum);
}
ll ans=0;
void solve(int pos,int end,ll sum){
	if(pos==end+1){
		ans+=M[key-sum];
		return;
	}
	solve(pos+1,end,sum+a[pos]);
	solve(pos+1,end,sum);
}
int main(){
	scanf("%d%lld",&n,&key);
	int i,j,k;
	for(i=1;i<=n;++i) scanf("%d",&a[i]);
	dfs(1,n/2,0);
	solve(n/2+1,n,0);
	cout<<ans;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值