题解 P3067 【[USACO12OPEN]平衡的奶牛群Balanced Cow S…】
题解
这道题算是一个折半搜索(meet in the middle)的好题
透彻
如果对折半搜索不太熟悉,可以先做一道较简单的题 [CEOI2015 Day2]世界冰球锦标赛
BZOJ链接或洛谷链接 附加my blog
这道题有三种状态
- 不放入任何集合
- 放入左边集合
- 放入右边集合
在搜索时如何表示呢,我们可以0,1,-1来表示,代码如下:
dfs(dep+1,sum);
dfs(dep+1,sum+v[dep]);
dfs(dep+1,sum-v[dep]);
但是我们得到的答案可能会有重复,就是我们可能把一个数选入左集合或右集合,但是都加入了状态,所以我们需要判重。
如何去判重,状态压缩,压成2进制去判重。
所以搜索时还要去记录状态,用一个
if(!vis[a[l].state|b[r].state])
vis[a[l].state|b[r].state]=1;//state记录二进制的选数状态 1表示选 0表示没选
最后要统计答案,排序后双指针扫描一遍即可。
注意,最后别忘了把0的那种方案减去。
code:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cctype>
#define ll long long
#define R register
#define N 22
using namespace std;
template<typename T>inline void read(T &a){
char c=getchar();T x=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
a=f*x;
}
int n,v[N<<1],maxdep,cnta,cntb;
bool vis[1<<N];
ll ans;
struct node{
int state,x;
}a[1<<N],b[1<<N];
inline bool cmp1(R node a,R node b){
return a.x<b.x;
}
inline bool cmp2(R node a,R node b){
return a.x>b.x;
}
inline void dfs(R int dep,R int sum,R int now,R int flg){
if(dep==maxdep+1){
if(!flg){
a[++cnta].x=sum;
a[cnta].state=now;
}
else{
b[++cntb].x=sum;
b[cntb].state=now;
}
return;
}
dfs(dep+1,sum,now,flg);
dfs(dep+1,sum+v[dep],now+(1<<(dep-1)),flg);
dfs(dep+1,sum-v[dep],now+(1<<(dep-1)),flg);
}
int main(){
read(n);
for(R int i=1;i<=n;i++)read(v[i]);
maxdep=n/2;dfs(1,0,0,0);
maxdep=n;dfs(n/2+1,0,0,1);
sort(a+1,a+1+cnta,cmp1);
sort(b+1,b+1+cntb,cmp2);
R int l=1,r=1;
while(l<=cnta&&r<=cntb){
while(-a[l].x<b[r].x&&r<=cntb)r++;
R int pos=r;
while(r<=cntb&&-a[l].x==b[r].x){
if(!vis[a[l].state|b[r].state]){
vis[a[l].state|b[r].state]=1;
ans++;
}
r++;
}
if(l<cnta&&a[l].x==a[l+1].x)r=pos;//相同让b数组的指针回来
l++;
}
printf("%lld\n",ans-1);//减去一个空集
return 0;
}