[USACO12OPEN]平衡的奶牛群,洛谷P3067,Meet in the Middle + Two-Pointers
阿新 • • 發佈:2018-11-09
正題
題目很明瞭。
看到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<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); }