题意:
给你32个物品,给定一个容积,让你恰好把这个背包装满,求出装满的方案
题解:
暴力计算的话,复杂度$2^{32}$肯定会炸,考虑一种类似bsgs的算法,先用$2^{16}$的时间遍历前一半物品的所有子集,将所得结果存进map里,再遍历后一半物品的子集,每得到一个解,在map里查询有没有相加正好得到背包大小的解。总时间复杂度$2^{16}log2^{16}=16*2^{16} \approx 1e6$
#include<iostream>
#include<map>
#define ULL unsigned long long
using namespace std;
void bind(int k,int n){
while(n--){
printf("%d",k&);
k>>=;
}
}
ULL a[];
map<ULL,int> mapp;
int main(){
int n;
ULL ans;
scanf("%d %lld",&n,&ans);
for(int i=;i<=n;i++)scanf("%lld",&a[i]);
int h=n/;
int w=<<h;
int i=;
while(!(i&w)){
ULL sum=;
for(int j=;j<=h;j++){
if((<<(j-))&i)sum+=a[j];
}
mapp[sum]=i;
i++;
}
w=<<(n-h);
i=;
while(!(i&w)){
ULL sum=;
for(int j=;j<=n-h;j++){
if((<<(j-))&(i))sum+=a[j+h];
}
map<ULL,int>::iterator it=mapp.find(ans-sum);
if(it!=mapp.end()){
bind(it->second,h);
bind(i,n-h);
printf("\n");
break;
}
i++;
}
return ;
}
拓展知识:背包公钥密码体系
背包公钥密码体系是利用背包问题(子集和问题)的NP完全性保证安全性的密码体系。
私钥持有者Alice找一个超递增序列,即某位大于前面所有位之和。比如M={3,11,24,50,115}
在超递增序列上求解求解背包是十分容易的,只需从后往前贪心即可,此结论留给读者自证。
私钥部分除了超递增序列,还有一个乘数r和模数m,需要保证r在模m意义下有逆元。通过私钥计算公钥的过程实际上是把超递增序列伪装成一个非超递增序列的过程。
伪装过程,就是把M的每一位乘r模m。比如令r=113,m=250,则M'=M*113%250={89,243,212,150,245}
这个M'就是公钥。
Bob获得了公钥,要给Alice发送一段消息,假设明文为x={1,0,1,0,1},将公钥和明文做个内积就得到了密文,s=1*89+0*243+1*212+0*150+1*245=546
Alice知道了密文后,将密文模乘r的逆元,$s*r^{-1}$ $mod$ $m=546*117$ $mod$ $250=142$,再在M上贪心即可。
而第三方得知了公钥和密文后,需要花费$n*2^{n/2}$时间才可解出明文。