HZOJ 毛一琛

直接暴搜是$3^n$的,无法接受。

正解是$meet \ in \ the \ middle$,暴搜前n/2个数,每个数的状态有三种:都不选,选为A集合,选为B集合。那么我们可以维护两个集合的差。

设状态为sta,每个数选中为1(无论是A还是B集合都为1),否则为0。差为v。

将二元组(sta,v)插入Hash_map。

之后暴搜后n/2个数。同样统计出状态sta和差v。在Hash_map中查询差为v的二元组个数。同时用数组v[1<<11][1<<11]记录两个状态是否选择过去重。

#include<bits/stdc++.h>
#define LL long long
using namespace std;
bool v[1<<11][1<<11];
struct Hash_map
{
	int fi[2333333],ni[2333333],siz;
	LL key[2333333],val[2333333];
	inline void insert(int x,int y)
	{
		int k=(x%2333333+2333333)%2333333,i=fi[k];
		for(;i;i=ni[i])if(key[i]==x&&val[i]==y)return;
		i=++siz,key[i]=x,val[i]=y,ni[i]=fi[k],fi[k]=i;
	}
	inline int find(int x,int y)
	{
		int k=(x%2333333+2333333)%2333333,res=0;
		for(int i=fi[k];i;i=ni[i])
		if(key[i]==x&&!v[y][val[i]])v[y][val[i]]=1,res++;
		return res;
	}
}f;
int n,m[21];
LL ans=0;
void dfs(int now,int en,int sta,int vv)
{
	if(now==en+1)
	{
		if(en!=n)f.insert(vv,sta);
		else ans+=f.find(vv,sta);
		return;
	}
	dfs(now+1,en,sta<<1,vv);
	dfs(now+1,en,sta<<1|1,vv+m[now]);
	dfs(now+1,en,sta<<1|1,vv-m[now]);
}
signed main()
{	
//	freopen("in.txt","r",stdin);
//	freopen("1.out","w",stdout);
	
	cin>>n;for(int i=1;i<=n;i++)cin>>m[i];
	dfs(1,n/2,0,0);dfs(n/2+1,n,0,0);
	printf("%lld\n",ans-1);
}

 

posted @ 2019-10-15 16:41  Al_Ca  阅读(254)  评论(0编辑  收藏  举报
ヾ(≧O≦)〃嗷~