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