问题 C: 规划-题解

C: 规划

在此我们约定,以一个点往外扩充 \(j\) 格表示同时往左右移动 \(j\) 格,占用 \(2\times j\) 的格子。举个例子,扩充 \(3\) 格的面积是 \(2\times 3^2=18\),具体看第一个样例的第二个图形。

\(l_i\) 表示 \(a_i-a_{i-1}\)\(r_i\) 表示 \(a_{i+1}-a_{i}\)。我们可以得出,对于 \(2\le i\le n\)\(l_i=r_{i-1}\)

\(get(x)\) 表示扩充 \(x\) 格获得的面积。

选满表示当前点顶到两边两个点的其中一个。

我们可以把一个点扩充后的菱形抽象成一个圆,扩充格子就是圆的半径。这样或许更好理解。

十分做法

首先有一个简单的动态规划:设 \(f_{i,j}\) 表示前 \(i\) 个点,往外扩充 \(j\) 格,能够获得的最大面积。对于点 \(i\) 的上限,就是 \(\min(l_i,r_i)\) 。转移的话,暴力就是对于前面一个点,所有满足 \(j+k\le l_i\)\(f_{i,j}=\max{f_{i-1,k}}+get(j)\) 。也就是说,当前面积加上上一个点的 DP值。当然,如果状态不存在,就设为零就好了。这样可以获得 \(10\) 分的好成绩。

二十分做法

对于第 \(4\) 个子任务,可以推出如果有两个点,两个点各占一半肯定比一个点占完更优。因此可以得出结论:这 \(n\) 个点,肯定是 \(\frac{n+1}{2}\) 个点取满,其他点不取。这样一定是最优的。具体来说,随便举个例子,比如两点相隔 \(6\) ,那么两个点各占 \(3\) 获得的面积 \(=2*get(3)=2*2*3^2=36\)。然而如果一个点占 \(6\),那么面积 \(=get(6)=72\),明显更优。与十分做法相结合即可获得 \(20\) 分。

五十分做法

我们考虑前两个做法优化。对于十分做法,首先有一个显然的优化:前缀和优化。这个随便搞一搞就好了。当然正解可以不用这个优化。

接着我们发现这个与值域有关的方程非常麻烦,我们考虑优化。我们发现其实有很多状态是无用的,相当于这些状态转移了也不会对下一个状态和答案做出任何贡献,因此我们考虑剪枝这些无用状态。相当于对于每一个点,找到一个大小尽量小的集合 \(S_i\),表示这一个点最优扩充的空间一定在这个急河内。考场上,我就想:肯定是尽量多选一些选满的点,这样肯定更优。

因此对于每一个点,我们考虑选满这个点之后会对周围的点造成的影响,相当于选满这个点周围的点就紧挨着这个点,直到无法紧挨着,相当于换了一个连通块。我们把这些点的这些值全部放到集合 \(S_i\) 中,同时考虑一个点肯定还有不放、放满两种状态要考虑。

转移时就只用考虑这些状态就可以了。因为值域较大,我使用的是 \(map\) DP,前缀和优化,时间复杂度最坏情况 \(O(n^2\log_2(n))\)。考虑相结合二十分做法即可获得五十分。这里是为 AC 做法铺垫。

因此我在考场上使用这个做法获得了 \(40\) 分,剩下 \(10\) 分是因为INF设小了!!痛失10分

AC做法

其实 AC 做法就是五十分做法的优化。我们考虑其实对于集合 \(S_i\) ,还是有很多状态没有使用。那我们接着考虑剪枝。怎么还剪啊

对于每一个点,有两种情况必须考虑:当前点不选和当前点选满。接下来就是高危警告

先放结论。

对于每一个点,我们定义如果这个点的 \(l_i\le r_i\) ,那么我们用 \(<\) 号表示这个点,否则就用 \(>\) 号表示这个点。那么这个点集就可以表示为小于大于号组成的序列,比如 \(<>><<>><<><><<>>\)

那么接下来,我们先考虑**中间没有 > **的两个 \(>\) 。因此,中间肯定是一堆 \(<\) 。那么我们直接把最右边的 \(>\) 选满,中间的所有点就看看能不能跟右边的圆相切,能就放进当前点的集合。形象的,相当于对于一个 \(><<<<<<<<<>\) 的子区间,我们最右边的点选满,其他点尝试在右边点选满的基础上选满。

对于 \(<>>>>>>>>><\) 的子区间我们也类似的操作。

于是我们发现,最后每个集合的点最多只有 \(4\) 个,而这恰好包括了最优解。完结撒花 好吧,还没有完。对于每个点,我们发现集合恰好包括了最优解的所有可能。为什么呢?我也不知道

当然,只要知道这个结论你就可以做题了。具体操作就是要不然就是 \(map\) DP,要不然就直接 \(vector\) 暴力 DP 。事实证明两种做法都能 AC,但是我用了火车头。。注意时空优化!!!

下面是证明部分,由另一人执笔。

为了证明这个贪心,我们先做亿点点准备工作(

\(r_i\) 表示第 \(i\) 个圆的半径, \(p_i\) 表示第 \(i\) 个圆的圆心横坐标(即题目中的 \(x_i\))。

\(p_0=-\inf,p_{n+1}=\inf\)

结论一:

对于每一个点,\(r_i=0\)\(r_i=\min(p_i-p_{i-1},p_{i+1}-p_i)\) 在考虑范围内

显然。

结论二:

在最优情况中,每一个圆至少和旁边一个圆相切。

显然,如果不和周围圆相切,我们可以把 \(r_i\) 调大,而仍然合法。

结论三:

当一个圆和旁边一个圆相切,一个圆相离时(假设相切的圆是第 \(i+1\) 个)满足 \(r_i<r_{i+1}\)

考虑反证法,如果 \(r_i\ge r_{i+1}\),我们考虑把圆 \(i\) 调大 \(t\),并把圆 \(i+1\) 调小 \(t\),此时一定存在一个 \(t\) 使得调整后仍然合法。

考虑调整前后两个方案中这两个圆的贡献的差:

\(\Delta S=(r_i+t)^2+(r_{i+1}-t)^2-r_i^2-r_{i+1}^2=2t_6+2t(r_i-r_{i+1})>0\)

所以调整后一定更优,所以此时一定满足 \(r_i<r_{i+1}\)

结论四:

当一个圆和旁边两个圆都相切时,满足 \(r_i<\max(r_{i-1},r_{i+1})\)

考虑和结论三一样的证法,

不妨假设 \(r_{i-1}\le r_{i+1}\)

假设 \(r_i\ge r_{i+1}\),我们考虑把圆 \(i\) 调大使得经过 \(p_{i-1}\),则 \(r_{i-1}\) 会变成 \(0\)\(r_{i+1}\) 变成 \(r_{i+1}-r_{i-1}\)

\(\Delta S=(r_{i+1}-r_{i-1})^2+(r_i+r_{i-1})^2-r_{i-1}^2-r_i^2-r_{i+1}^2=r_{i-1}^2+2r_{i-1}(r_i-r_{i-1})>0\)

所以调整后一定更优,所以此时一定满足 \(r_i<\max(r_{i-1},r_{i+1})\)

接下来,我们考虑什么样的 \(r_i\in S_i\)

  • 对于 \(i=1/n\),取 \(0\) 或取顶满的情况,
  • 对于 \(i\in (1,n)\),假设 \(r_{i-1}\le r_{i+1}\),我们可以得到 \(r_i<r_{i+1}\)

证明:如果圆 \(i\) 之和右边相切,那么 \(r_i<r_{i+1}\)(结论三),如果圆 \(i\) 和两边都相切,那么也有 \(r_i<r_{i+1}\)(结论四),如果圆 \(i\) 和左边相切,那么 \(r_i<r_{i-1}<r_{i+1}\)

所以,对于所有情况,都有 \(r_i<r_{i+1}\)

接下来,我们考虑圆 \(i+1\) 右边界是否经过 \(p_{i+2}\),如果经过,那么圆 \(i+1\)> 的,并且加入有用状态;如果没经过,我们会和刚才一样,得到 \(r_{i+1}<r_{i+2}\),并且此时会发现 \(p_{i+1}-p_i=r_i+r_{i-1}<r_i+r_{i+1}=p_{i+2}-p_{i+1}\),因此圆 \(i+1\)< 的,并继续考虑圆 \(i+3\)

因此,我们会发现这样有用的状态会是一段 <<<<<<<>,其中左边的一个状态未定,所以可以是 ><<<<<<<<>。如果把整个序列反过来,我们发现 <>>>>>>>>>>< 也将是有用状态。

证毕!

那么这道题就圆满结束。

posted @ 2022-03-09 08:08  编程客  阅读(37)  评论(0编辑  收藏  举报