https://ac.nowcoder.com/login?callBack=%2Facm%2Fcontest%2F889%2FD%3F%26headNav%3Dacm
题意:给定长度为n 的数组{a i }和总和sum。
请找到{a i }的子集,使得子集的总和为sum,输出选取方案的01序列。
分析:可以看成超大背包,当成普通背包做的话时间复杂度是n*a[ i ] (max) ,而 a[i] 太大,显然会超时,当成暴力枚举来做的话,2^n 也超时,但是n只有36,利用折半枚举,在指数上折半的话就可以大幅减少时间复杂度。
在进行第二次枚举的查找时,因为数太大就用了set,以及set的find() != set.end() 来实现查找是否存在,而在输出选取方案的01序列时,因为还得输出第一次枚举的方案,所以第一次枚举时用map来保存了state和sum。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<set> #include<map> using namespace std; typedef long long ll; int n,q,p; ll sum; ll a[39]; set<ll> se; map<ll,int> ma; int main(){ scanf("%d%lld",&n,&sum); p=n>>1,q=n-(n>>1); for(int i=1; i<=n; i++){ scanf("%lld",a+i); } for(int i=1; i<(1<<p); i++){ ll res=0; for(int j=0; j<p; j++){ if(i&(1<<j)) res+=a[j+1]; } se.insert(res); ma[res]=i; } for(int i=1; i<(1<<q); i++){ ll res=0; for(int j=0; j<q; j++){ if(i&(1<<j)) res+=a[p+j+1]; } if(se.find(sum-res)!=se.end()){ int state=ma[sum-res]; for(int k=0; k<p; k++){ if(state&(1<<k)) putchar('1'); else putchar('0'); } for(int k=0; k<q; k++){ if(i&(1<<k)) putchar('1'); else putchar('0'); } puts(""); return 0; } } }