欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

折半搜索 - 某种密码

程序员文章站 2024-03-20 18:11:28
...

某种密码

描述
关于某种密码有如下描述:某种密码的原文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不行就从nn下手
那么小的数据范围,暴力搜索吧
但如果只是暴力搜索的话,2402^{40}貌似行不通
怎么办怎么办?哈……2402^{40}不行,一半总可以搜出来吧
那我们就折半搞(以后这种数据范围好像可以搜,又好像会挂,就可以试试折半了)
将物品集合均分成两个交集为空,补集为全集的集合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;
}
相关标签: 折半搜索