Loading

CF1239E 题解

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);
}
posted @ 2023-08-08 22:12  Albertvαn  阅读(6)  评论(0编辑  收藏  举报