CF1239E
给定 \(2n\) 个数,将其重排成 \(2\times n\) 的矩阵,最小化:从 \((1,1)\) 走到 \((2,n)\),只可向右下走的所有方案中,途径所有数的和的最大值。\(n\le 25,|V|\le 5\times 10^4\)。
考场上有个 \(n\le 10\) 的包,分值高达 \(40\)。注意到 \(\binom{20}{10} \approx 10^5\) 可枚举,考虑确定第一行选取元素集合 \(a\) 和第二行 \(b\) 之后,如何将其排列。
这是个典,见此。令 \(s=a_1+\sum b_i\),即在第 \(1\) 列向下走的情况,答案就是
\[s+\max_{k=1}^{n-1}\{\sum_{i=1}^k a_{i+1}-b_i\} \]也就是对 \(a_{i+1}-b_i\) 的前缀和取 \(\max\)。我们要令这个东西最小,那显然是 \(a\) 从小到大排,\(b\) 从大到小排。看大样例就盯出来了。
官方题解给的是这个图。\(a_{i+1}-b_i\) 是单调递增的。那么答案就是
\[\max\left(a_{\min}+\sum b_i,b_{\min}+\sum a_i\right) \]回到原问题,将给出的长为 \(2n\) 的数组 \(r\) 排序,现在要把它划分成 \(a\) 和 \(b\) 并最小化上式。
首先不妨 \(a_{\min}\le b_{\min}\)。那么有结论,\(a_{\min}=r_1,b_{\min}=r_2\)。证明就是把它们替换成 \(r_k(k\ge 3)\) 然后发现答案更劣。
注意到
\[\left(a_{\min}+\sum b_i\right)+\left(b_{\min}+\sum a_i\right)=S=r_1+r_2+\sum r_i \]为定值,让两者尽量靠近 \(\frac S2\) 即可。也就是
\[\sum_{i=2}^n a_i\sim \lceil \left(r_1+r_2+\sum r_i\right)\div 2\rceil-r_1-r_2 \]变成背包问题:在 \(r_{3,\cdots,2n}\) 中选出 \(n-1\) 个数,使其加和尽量靠近上述值 \(v\),并记录方案。
令 \(f_{i,s,c}:\) 考虑到 \(r_i\),加和为 \(s\),选中了 \(c\) 个数,是否可行。朴素转移的时空均为 \(\Theta(n^3|V|)\)(这里的 \(s\) 是 \(\Theta(n|V|)\) 级别的)。
for(int i=3;i<=(n<<1);++i) for(int s=W-1;s>=a[i];--s) for(int c=n-1;c;--c)
if(f[s-a[i]][c-1]) f[s][c]=1,l[s][c]=i,nxt[i][s][c]=l[s-a[i]][c-1];//f运用了滚动数组,l和nxt记录方案
考虑优化空间(记录方案)。一眼滚动数组,把 \(i\) 那一维扔了。但是有问题,对于一对 \((s,c)\),满足 \(f_{i,s,c}=1\) 的 \(i\) 不止一个,直接滚动数组会导致永远记录最后一个 \(i\),0/1 背包变成完全背包。
所以只在 \(f_{i,s,c}\) 第一次变成 \(1\) 的时候记录方案:
for(int i=3;i<=(n<<1);++i) for(int s=W-1;s>=a[i];--s) for(int c=n-1;c;--c)
if(!f[s][c]) if(f[s-a[i]][c-1]) f[s][c]=1,l[s][c]=i,nxt[s][c]=l[s-a[i]][c-1];
空间复杂度 \(O(n^2|V|)\)。调整一下,从记录下标变成记录值:
for(int i=3;i<=(n<<1);++i) for(int s=W-1;s>=a[i];--s) for(int c=n-1;c;--c)
if(!f[s][c]) if(f[s-a[i]][c-1]) f[s][c]=1,l[s][c]=a[i];
再考虑优化时间。调整下标顺序,然后注意到 \(f_{i,c}\) 是一个 0/1 数组,可以拿 bitset
搞。错位或通过左移实现。求新增的位就是异或一下。时间变成 \(O(\frac{n^3|V|}w)\),且这个 \(w\) 比 \(n\) 还大。
bitset<W> f[25],tf;
for(int i=3;i<=(n<<1);++i) for(int c=n-1;c;--c){
tf=f[c];f[c]|=(f[c-1]<<a[i]);tf^=f[c];
for(int x=tf._Find_first();x!=W;x=tf._Find_next(x)) l[x][c]=a[i];
}
完整代码:
#include <cstdio>
#include <bitset>
#include <algorithm>
using namespace std;
const int W=50000*25+5;
int a[55],l[W][25],t[W>>4],stk[55],tp;
bitset<W> f[25],tf;
int main()
{
int n,s=0;scanf("%d",&n);
for(int i=1;i<=(n<<1);++i) scanf("%d",a+i),s+=a[i],++t[a[i]];
sort(a+1,a+(n<<1)+1);int am=a[1],bm=a[2];--t[am];--t[bm];
s=((s+am+bm+1)>>1)-am-bm;f[0][0]=1;
for(int i=3;i<=(n<<1);++i) for(int c=n-1;c;--c){
tf=f[c];f[c]|=(f[c-1]<<a[i]);tf^=f[c];
for(int x=tf._Find_first();x!=W;x=tf._Find_next(x)) l[x][c]=a[i];
}
for(int d=0;;++d) if(f[n-1][s-d]||f[n-1][s+d]){
int x,su,c=n-1;
f[c][s-d]?(x=l[s-d][c],su=s-d):(x=l[s+d][c],su=s+d);
while(c) stk[++tp]=x,--t[x],x=l[su-=x][--c];
break;
}
printf("%d ",am);while(tp) printf("%d ",stk[tp--]);
puts("");for(int i=50000;~i;--i) while(t[i]--) printf("%d ",i);
printf("%d",bm);
}
标签:25,right,min,int,题解,sum,CF1239E,--
From: https://www.cnblogs.com/vanspace/p/CF1239E.html