第一眼看到这题,以为可以直接大力搜索,T了一发以后发现时间复杂度为 O(320) ,于是就想折半搜索。
然而直接折半搜索WA了一发以后我停下了盲目的敲代码,仔细思考了题目。造了一组数据:“6 88 36 36 7 95 24”,发现{88,36,7}与{36,95}有两种组合情况,但是答案只会被加一次。
于是想到状压,判断两个相等的集合内取了哪些数,开个bool数组去一下重即可。
附上AC代码:
# pragma GCC optimize "O3"
# pragma G++ optimize "O3"
#include <cstdio>
#include <algorithm>
#include <map>
#include <vector>
using namespace std;
map <int,vector <int> > f;
int n,a[21],ans;
bool b[1048580];
inline void so1(int l,int r,int sum,int s){
if (l>r){
f[sum].push_back(s);
return;
}
so1(l+1,r,sum,s);
so1(l+1,r,sum+a[l],s+(1<<l-1));
so1(l+1,r,sum-a[l],s+(1<<l-1));
return;
}
inline void so2(int l,int r,int sum,int s){
if (l>r){
for (int i=0; i<f[-sum].size(); ++i)
if (!b[s|f[-sum][i]]) b[s|f[-sum][i]]=1,++ans;
return;
}
so2(l+1,r,sum,s);
so2(l+1,r,sum+a[l],s+(1<<l-1));
so2(l+1,r,sum-a[l],s+(1<<l-1));
return;
}
int main(void){
scanf("%d",&n);
for (int i=1; i<=n; ++i) scanf("%d",&a[i]);
so1(1,n>>1,0,0),so2((n>>1)+1,n,0,0),printf("%d\n",ans-1);
return 0;
}
但是!!!
上述代码可能TLE,实测BZOJ数据跑出来需要10576ms,卡时限AC……QAQ
于是想到计算答案的那个搜索调用STL次数太多,需要用一些常数较小的数据结构来优化一下。由map想到了手打hash map。
于是orz zzk,大佬教我打hash map,太强啦。
附上AC代码:
#include <cstdio>
#include <vector>
using namespace std;
typedef long long ll;
const int m=0x7fffffff,mod=999917;
int n,a[21],ans;
int num,h[mod],nt[59050];
vector <int> f[59050];
ll w[59050];
bool b[1<<20];
inline void ist(ll sum,int s){
for (int i=h[(sum+=m)%mod]; i; i=nt[i]) if (w[i]==sum) return f[i].push_back(s);
return (void)(w[++num]=sum,f[num].push_back(s),nt[num]=h[sum%mod],h[sum%mod]=num);
}
inline int find(ll sum){
for (int i=h[(sum+=m)%mod]; i; i=nt[i]) if (w[i]==sum) return i;
return 0;
}
inline void so1(int l,int r,int sum=0,int s=0){
if (l>r) return ist(sum,s);
return so1(l+1,r,sum,s),so1(l+1,r,sum+a[l],s+(1<<l-1)),so1(l+1,r,sum-a[l],s+(1<<l-1));
}
inline void so2(int l,int r,int sum=0,int s=0){
if (l>r){
for (int i=find(-sum),j=0; j<f[i].size(); ++j)
if (!b[s|f[i][j]]) b[s|f[i][j]]=1,++ans;
return;
}
return so2(l+1,r,sum,s),so2(l+1,r,sum+a[l],s+(1<<l-1)),so2(l+1,r,sum-a[l],s+(1<<l-1));
}
int main(void){
scanf("%d",&n);
for (int i=1; i<=n; ++i) scanf("%d",&a[i]);
return so1(1,n>>1),so2((n>>1)+1,n),printf("%d\n",ans-1),0;
}