原题传送门:Cutting Carrot
题目分析
这题的关键在于不要纠结于局部,而是注重整体。这也是许多看似复杂困难的问题的解法。我们通过追问的方式给出我的思考过程。
- Q1:座位的排布是否有某种规律?
- A1:可以分成一段段,段与段之间由客人隔开。
- Q2:既然要求最小值,那段数能否先把它搞到最小?
- A2:很遗憾不行。但幸运的是,段数是一个定值。具体而言,每一个圆桌上如果有 m ∈ [ 1 , N ] m\in[1,N] m∈[1,N] 个人,那么也将有 m m m 个段(圆排列的性质),因此总段数 = N =N =N。
- Q3:那是否有哪些段“阻碍”了最小化?
- A3:如果某个乘客的舒适空间特别大,那么这一段的最小长度就要大于等于它的舒适空间(非严格表述)。
- Q4:那么怎么省空间呢?
- A4:只能左右把空位“抵消”掉,从而分担掉一点舒适空间。
至此,我们已经可以给出一个数学化的表述:假设一个人的左舒适空间为 l i , 1 ≤ i ≤ N l_i, 1\leq i\leq N li,1≤i≤N, 右舒适空间为 r i , 1 ≤ i ≤ N r_i, 1\leq i\leq N ri,1≤i≤N, σ \sigma σ 为 { 1 , 2 , ⋯ , N } \{1, 2, \cdots, N\} {1,2,⋯,N} 上的任一置换, 则问题的一种局部解为 ∑ i = 1 n max { l i , r σ ( i ) } + N 加上一开始的 N 个人 ⏟ \sum\limits_{i=1}^{n}\max\{l_i, r_{\sigma(i)}\}+\underset{\underbrace{\text{加上一开始的 N 个人}}}{N} i=1∑nmax{li,rσ(i)}+ 加上一开始的 N 个人N。这里若 σ g ( i ) = i \sigma^{g}(i)=i σg(i)=i, 那么就成了一个圈( 1 ≤ g ≤ N 1\leq g\leq N 1≤g≤N),简单来说,这种状态一定能构造出来。
容易看出,为了使上式达到最小值,应该让大的和大的配,小的和小的配(直观上也很显然,严格的数学证明只要写一个 2 × N 2\times N 2×N 阵列去分析就行)。于是如果将 { l i } , { r i } \left\{ l_i \right\} ,\left\{ r_i \right\} {li},{ri} 排序为 r 1 ≥ r 2 ≥ ⋯ ≥ r N , l 1 ≥ l 2 ≥ ⋯ ≥ l N r_1\geq r_2\geq \cdots \geq r_N, l_1\geq l_2\geq \cdots \geq l_N r1≥r2≥⋯≥rN,l1≥l2≥⋯≥lN, 则所求的 N + min { ∑ i = 1 n max { l i , r σ ( i ) } } = N + ∑ i = 1 n max { l i , r i } N+\min\{\sum\limits_{i=1}^{n}\max\{l_i, r_{\sigma(i)}\}\} = N+\sum\limits_{i=1}^{n}\max\{l_i, r_i\} N+min{i=1∑nmax{li,rσ(i)}}=N+i=1∑nmax{li,ri}。剩下的代码是容易的。
代码(python 3)
class Solution:
def __init__(self) -> None:
pass
def solution(self, n, vector):
left = [vector[i][0] for i in range(n)]
right = [vector[i][1] for i in range(n)]
left.sort()
right.sort()
result = n
for i in range(n):
result = result + max(left[i], right[i])
return result
if __name__ == "__main__":
n = int(input().strip())
vector = []
for i in range(n):
vector.append([int(item) for item in input().strip().split()])
sol = Solution()
result = sol.solution(n, vector)
print(result)