[USACO12OPEN]平衡的奶牛群
题目很明了。
看到n才20.
暴力,枚举一个点在左边,在右边还是不放。记录一下每个状态是否被记录过(放左边和放右边都是放,所以要开一个state来记录这个取和不取的状态是否被记录过
发现是,好大啊。
优化搜索想到Meet in the Middle。
接着,我们就可以处理出左边的状态和右边的状态。
这个状态是指什么呢?
指的是选出的放右边的减去选出的放左边的差。
那么两边的状态如果存在a和b,并且a+b=0。这一组就是答案,最后减去1即可。空集不算。
问题就在于怎么寻找a+b=0的状态个数。
首先我们可以对左边的状态排一次序,然后用右边的b来找一个左边的a,使得a=-b。二分查找可以完成。
但是有更快的方法。
把a从小到大排序,把b从大到小排序,那么a递增,b也单调递减,所以就直接上Two-Pointers。
注意两个a相同要重新算一遍。
#include
#include
#include
#include
#include
using namespace std;
int n;
int s[25];
struct node{
int x,s;
}a[60010],b[60010];
bool tf[1050000];
bool cmp1(node x,node y){
return x.xy.x;
}
void dfs(int x,int y,int l,int r,int sta,node*now){
if(x>y){
now[++now[0].x].x=r-l;
now[now[0].x].s=sta;
return ;
}
dfs(x+1,y,l,r,sta,now);
dfs(x+1,y,l+s[x],r,sta|(1<<(x-1)),now);
dfs(x+1,y,l,r+s[x],sta|(1<<(x-1)),now);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&s[i]);
int mid=(1+n)/2;
dfs(1,mid,0,0,0,a);
dfs(mid+1,n,0,0,0,b);
sort(a+1,a+1+a[0].x,cmp1);
sort(b+1,b+1+b[0].x,cmp2);
int ans=0;
int l=1,r=1;
while(l<=a[0].x && r<=b[0].x){
int pos=r;
while(r<=b[0].x && a[l].x+b[r].x>=0){
if(a[l].x+b[r].x==0 && !tf[a[l].s|b[r].s]) ans++,tf[a[l].s|b[r].s]=true;
r++;
}
if(l!=a[0].x && a[l+1].x==a[l].x) r=pos;
l++;
}
printf("%d\n",ans-1);
}