[USACO12OPEN]平衡的奶牛群,洛谷P3067,Meet in the Middle + Two-Pointers

版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢 https://blog.csdn.net/Deep_Kevin/article/details/83831576

正题

      [USACO12OPEN]平衡的奶牛群

      题目很明了。

      看到n才20.

      暴力,枚举一个点在左边,在右边还是不放。记录一下每个状态是否被记录过(放左边和放右边都是放,所以要开一个state来记录这个取和不取的状态是否被记录过

      发现是3^n,好大啊。

      优化搜索想到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<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
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.x<y.x;
}

bool cmp2(node x,node y){
	return x.x>y.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);
}

猜你喜欢

转载自blog.csdn.net/Deep_Kevin/article/details/83831576