题目描述
$n$ 堆石子,第 $i$ 堆有 $a_i$ 个石子。将 $n$ 堆石子合并成三堆,使得这三堆的极差最小。
数据范围
$n \le 24,a_i \le 10^9$ 。
题解
考虑折半搜索,考虑如何将两边的三元组合并得到答案。
考虑两边的三元组分别为 $(a_1,b_1,c_1)$ 和 $(a_2,b_2,c_2)$ ,那么将它们合并后为 $(a_1+a_2,b_1+b_2,c_1+c_2)$ ,为了方便我们令 $a_1+a_2 \ge b_1+b_2 \ge c_1+c_2$ ,这样这两组的贡献为 $a_1+a_2-c_1-c_2$ 。
于是我们可以令 $f=a-b,g=b-c$ ,上述条件就变为要求 $f_1+f_2,g_1+g_2 \ge 0$ ,求 $f_1+f_2+g_1+g_2$ 最小值,所以我们可以按照 $f$ 排序进行双指针, $g$ 用树状数组维护即可。
效率 $O(NlogN)$ ,其中 $N=3^{\frac{n}{2}}$ 。
代码
#include <bits/stdc++.h> #define LL long long using namespace std; const int N=6e5+5; int n,m,a[25],F,G; LL b[N],s[N],A=2e18; struct O{LL x,y;}f[N],g[N]; void dfs(int x){ if (x>m){ if (m<n) f[++F]=(O){s[0]-s[1],s[1]-s[2]}; else g[++G]=(O){s[0]-s[1],s[1]-s[2]}; return; } for (int i=0;i<3;i++) s[i]+=a[x],dfs(x+1),s[i]-=a[x]; } bool cmp(O x,O y){return x.x<y.x;} #define lb(z) lower_bound(b+1,b+m+1,z)-b void upd(int x,LL v){ for (;x;x-=x&-x) s[x]=min(s[x],v); } LL qry(int x){ LL v=2e18; for (;x<=m;x+=x&-x) v=min(v,s[x]); return v; } int main(){ cin>>n; for (int i=1;i<=n;i++) scanf("%d",&a[i]); m=n/2;dfs(1);m=n;dfs(n/2+1); for (int i=1;i<=G;i++) b[i]=g[i].y; sort(b+1,b+G+1);m=unique(b+1,b+G+1)-b-1; sort(f+1,f+F+1,cmp);sort(g+1,g+G+1,cmp); for (int i=1;i<=m;i++) s[i]=2e18; for (int i=1,j=G;i<=F;i++){ while(j && f[i].x+g[j].x>=0) upd(lb(g[j].y),g[j].x+g[j].y),j--; A=min(A,qry(lb(-f[i].y))+f[i].x+f[i].y); } cout<<A<<endl;return 0; }