3n片披萨

Let’s discuss an algorithm problem. You can find the problem here on leetcode: https://leetcode.com/problems/pizza-with-3n-slices/. There is no editorial, so I am writing one.

让我们讨论一个算法问题。 您可以在leetcode上找到问题所在: https ://leetcode.com/problems/pizza-with-3n-slices/。 没有社论,所以我正在写一篇。

问题陈述 (The Problem Statement)

Given a pizza consisting of 3*N slices where N ∈ Integer. Each slice of pizza has some reward points associated with it. Alice wants to eat exactly N slices, whichever slice she eats, neighbouring slices on both sides disappear (or eaten away by someone else). Please note that the neighbouring slice doesn’t have to be adjacent but the closest available slice in both clockwise and anticlockwise directions. Alice gets reward points when she eats a slice, what is the maximum number of points that Alice can collect?

给定一个由3 * N片组成的披萨,其中N∈整数。 每一块比萨饼都有一些奖励积分。 爱丽丝想精确地吃掉N片,无论她吃了哪一块,两侧的相邻片都消失了(或被其他人吃掉了)。 请注意,相邻切片不必相邻,而是在顺时针和逆时针方向上都可以使用最近的切片。 爱丽丝吃一片时可获得奖励积分,爱丽丝最多可以收集多少积分?

Let’s understand the problem with an example:

让我们用一个例子来理解这个问题:

Image for post

There are 6 slices meaning that Alice needs to choose 2 slices out of them. Let’s say she chooses C, slices B and D will disappear and Alice bags 100 points. After it, she again chooses E and bagging a total of 200 points.

有6个切片,这意味着Alice需要从中选择2个切片。 假设她选择C,切片B和D将消失,而Alice则得到100分。 之后,她再次选择E并获得200分。

Please try to solve the problem yourself before scrolling down to the solution. And remember, pen and paper always helps!

向下滚动到解决方案之前,请尝试自己解决问题。 请记住,笔和纸总是有帮助的!

贪婪的方法 (The Greedy Approach)

I always start trying out the greedy approach. If it works, it leads to the easiest and the quickest implementations. The only catch is that it takes some proving and reasoning skills to feel confident that your greedy approach will work for all cases. One approach here could be:

我总是开始尝试贪婪的方法。 如果可行,它将导致最简单,最快的实现。 唯一要注意的是,需要一些证明和推理技巧才能确信您的贪婪方法适用于所有情况。 这里的一种方法可能是:

total_points = 0
for i in range(N):
selected_slice = slice with max points from available slices
total_points += selected_slice.points
return total_points

The question is, would this approach always help Alice fetch the maximum possible score? No, it won’t. It is not hard to find an example where this approach doesn’t find the optimal result. The problem is that selecting a slice rejects two slices around the selected slice, this approach discards neighbours even without considering their worth. It is possible that the worth of two neighbours exceeds the worth of selected slice and there is nothing else in the rest of the pizza to compensate for the loss. Here is a concrete example:

问题是,这种方法是否总是可以帮助Alice获取最大的分数? 不,不会。 不难发现这种方法找不到最佳结果的例子。 问题在于选择一个切片会拒绝所选切片周围的两个切片,即使不考虑邻居的价值,该方法也会丢弃邻居。 两个邻居的价值可能超过了所选切片的价值,而其他比萨饼中没有其他东西可以弥补损失。 这是一个具体的例子:

Image for post

Now you can clearly see here that selecting 100 would discard both slices with 90 points grabbing only a total of 110 points whereas it was possible to select 180 points by selecting the two 90 points slices.

现在,您可以在此处清楚地看到,选择100将丢弃两个具有90个点的切片,而这些切片仅获取110个点,而可以通过选择两个90点的切片来选择180个点。

蛮力法 (The Brute Force Approach)

This is the second approach you always want to try when greedy doesn’t help. This approach entails enumerating all possibilities, calculating reward points that Alice will get for each of those possibilities and comparing them to find the maximum.

当贪婪无济于事时,这是您始终想尝试的第二种方法。 这种方法需要枚举所有可能性,计算爱丽丝将为每种可能性获得的奖励积分,并进行比较以找到最大值。

How many possibilities: To start with Alice has 3N slices to choose from. After she selects a slice, she has (3N - 3) slices to choose from. So extending the series, she will have a total of 3N*(3N - 3)*(3N - 6)…(N times) possibilities which is roughly equal to (3N^N) possibilities.

有多少种可能性:从Alice开始,有3N个切片可供选择。 选择切片后,她有(3N-3)个切片可供选择。 因此,扩展该系列,她将总共拥有3N *(3N-3)*(3N-6)…(N倍)种可能性,大约等于(3N ^ N)种可能性。

How much time finding out points gathered in one of those possibilities: It will take O(N) time to go through the chosen slices and finding the sum. You can keep track of the maximum as you are doing the sum.

在这些可能性之一中找出要收集的点需要多少时间:花费O(N)时间来遍历选定的切片并找到总和。 在进行总和时,您可以跟踪最大值。

Total running time complexity: O(3N^N)*N

总运行时间复杂度:O(3N ^ N)* N

Clearly, the brute force solution doesn’t scale well and wouldn’t work even for small values of N. Practically speaking, exponential algorithms are as useful as the swiss knife in the nail-cutter.

显然,蛮力解决方案不能很好地扩展,即使对于较小的N也不起作用。实际上,指数算法与指甲钳中的瑞士刀一样有用。

最佳方法 (The Optimal Approach)

When stuck at trying to find an optimal solution to the problem, it’s always helpful to start writing down the observations and start cutting down the possibilities (a.k.a. pruning) from all the possibilities that we considered in our brute force solution.

当试图解决问题的最佳解决方案时,开始写下观察结果并从我们在蛮力解决方案中考虑的所有可能性开始减少可能性(又称修剪)总是有帮助的。

One of the most obvious observations is that given a state of pizza, Alice can never eat two neighbouring slices. Given this observation, the best we can do is to pick the subset of “non-neighbouring” slices such that the sum of points of those slices is maximum possible. For the sake of simplicity and the lack of creativity, let’s call such a subset - the candidate subset. Please remember that there are few important things to remember about the “candidate subset”: (a) It contains exactly N slices (b) None of those slices is adjacent in the original pizza and (c) It is the subset with maximal points among all subsets that follow (a) and (b). The important question that immediately follows the statement we just made:

最明显的发现之一是,在披萨状态下,爱丽丝永远不能吃两个相邻的薄片。 鉴于此观察,我们最好的办法是选择“非相邻”切片的子集,以使这些切片的点之和最大。 为了简单和缺乏创造力,我们称此类子集为候选子集。 请记住,关于“候选子集”,没有什么要记住的重要事情:(a)它正好包含N个切片(b)这些切片在原始比萨中都不相邻,并且(c)是其中最大点的子集(a)和(b)之后的所有子集。 我们刚刚发表的声明紧随其后的重要问题:

Is it always possible to pick up the candidate subset? It certainly not obvious. Please note that the neighbouring slices keep changing when we pick a slice and we may end up making two slices in the candidate subset neighbours of each other in an intermediate step. Example: Let’s say we had (A, B, C, D, E, F, G, H, I) and our candidate subset is (C, A, E). It’s a valid candidate subset because none of these is adjacent but when you pick C, you end up making A and E neighbours and thus you will never be able to pick both of them. For the same example, if we pick slices in order (A, C, E) - we are able to pick them up. So need to prove that there always exists an order that doesn’t end up making two selecting slices neighbours of each other in any intermediate state.

是否总是有可能挑选候选子集? 当然不是很明显。 请注意,当我们选择一个切片时,相邻切片会不断变化,在中间步骤中,我们可能最终会在彼此的候选子集​​中创建两个切片。 示例:假设我们有(A,B,C,D,E,F,G,H,I),而我们的候选子集​​为(C,A,E)。 这是一个有效的候选子集​​,因为这些都不是相邻的,但是当您选择C时,最终将成为A和E邻居,因此您将永远无法选择它们两者。 对于同一示例,如果我们按顺序(A,C,E)选取切片,则可以拾取它们。 因此,需要证明始终存在一个不会以任何中间状态使两个选择切片彼此相邻的顺序。

The proof is fairly simple. Consider the elements which aren’t selected in the candidate subset. There are 2N such elements spread among our N selected elements. If you place only the selected slices on the pizza box, there are N “gaps” that we need to fill up with these 2N not-selected elements. With the help of the pigeonhole principle, we can argue that there is at least one “gap” that we need to fill up with ≥2 non-selected slices. Picking up a slice from the end of such a gap will leave at least one non-selected slice between two selected slice making sure that two selected slices don’t end up being adjacent. We can apply this reasoning inductively (induction is to the world of proofs what recursion is to programming) and prove that there will always exist a way to select the candidate subset.

证明很简单。 考虑在候选子集中未选择的元素。 在我们选择的N个元素中分布了2N个这样的元素。 如果仅将选定的切片放在披萨盒上,则需要用这2N个未选定的元素填充N个“间隙”。 借助信鸽原理,我们可以说至少有一个“缺口”需要填充≥2个未选择的切片。 从这样的间隙的末端拾取一个切片将在两个选定的切片之间留下至少一个未选定的切片,以确保两个选定的切片最终不会相邻。 我们可以归纳地应用这种推理(归纳到证明世界,递归就是编程),并证明总是存在选择候选子集的方法。

Great. Now, all we need is to find an optimal way to select the candidate subset. Let’s move on…

大。 现在,我们所需要做的就是找到一种选择候选子集的最佳方法。 让我们继续前进…

The problem we have at hand is: Find the maximum subset of integers such that no two selected integers are adjacents to each other in a circular array.

我们面临的问题是:找到整数的最大子集,以使两个选定的整数在圆形数组中彼此不相邻。

It is a standard DP problem that you might have already solved. But have you? In this case, the problem has to be solved on a circular array which makes a little bit different than solving it in a linear array. The difference being that the first and the last elements can’t be chosen together whereas, in case of the linear array, there is nothing that we do to make sure that both the first and the last elements aren’t chosen at the same time. BTW, if you haven’t solved the linear version, please go through this: https://medium.com/@arunistime/maximum-sum-of-non-adjacent-numbers-algorithm-explained-159f08b5790a

这是您可能已经解决的标准DP问题。 但是你呢? 在这种情况下,必须在圆形阵列上解决该问题,这与在线性阵列中解决它有点不同。 区别在于不能同时选择第一个和最后一个元素,而对于线性数组,我们无法做任何事情来确保不会同时选择第一个和最后一个元素。 顺便说一句,如果您尚未解决线性版本,请检查以下内容: https : //medium.com/@arunistime/maximum-sum-of-non-adjacent-numbers-algorithm-explained-159f08b5790a

How do we solve the same problem for a circular array? A cheap solution is to consider two cases: a) when the first element is not selected and solve for linear array -> arr[1:N-1] b) when the last element is not selected and solve for the linear array -> arr[0:N-2], take a maximum of both cases and you are done. But wait, let’s not go for a cheap solution. There is a more interesting way to solve this.

我们如何解决圆形阵列的相同问题? 一种便宜的解决方案是考虑以下两种情况:a)未选择第一个元素并求解线性数组-> arr [1:N-1] b)未选择最后一个元素并求解线性数组-> arr [0:N-2],最多两种情况都可以完成。 但是,等等,我们不要寻求便宜的解决方案。 有一种更有趣的方法可以解决此问题。

I am going to make a statement — the minimum element from the array can never be in the candidate subset or if there are multiple minimum elements then at least one of them can never be in the candidate subset. If what I said is true, the solution is to simply open up the circular array such that the last element is the minimum element and now because we never need to select the minimum element, we will never need to select the last element and you can solve just for the linear array arr[0:N-2]. Voila!

我要发表一个声明—数组中的最小元素永远不能在候选子集中,或者如果有多个最小元素,那么它们中至少有一个永远不能在候选子集中。 如果我说的是正确的,解决方案是简单地打开圆形数组,使最后一个元素成为最小元素,现在因为我们不需要选择最小元素,所以我们将不再需要选择最后一个元素,并且您可以仅针对线性数组arr [0:N-2]求解。 瞧!

But how on earth do we prove that the minimum element will never be a part of the candidate subset? You can prove it by contradiction. Let’s assume that the minimum element IS the part of the candidate subset. Having said that, we will make logical arguments to reach an illogical conclusion. Reaching an illogical conclusion means that our assumption was wrong, proving that the minimum element shouldn’t be the part of the candidate subset. I know it’s too interesting for me to just write it down here for you guys to read, please try the proof yourself and share it in the comments. I will edit the post and add the proof here next week!

但是到底如何证明最小元素永远不会成为候选子集的一部分呢? 您可以通过矛盾证明这一点。 假设最小元素是候选子集的一部分。 话虽如此,我们将提出合乎逻辑的论据以得出不合逻辑的结论。 得出不合逻辑的结论意味着我们的假设是错误的,证明最小元素不应成为候选子集的一部分。 我知道对我来说太有趣了,只在这里写下来供大家阅读,请自己尝试打样并在评论中分享。 我将在下周编辑帖子并添加证明!

Have a great day!

祝你有美好的一天!

Edit 1: Putting up remaining proof — As we said, it can be proved by contradiction. Let’s say the minimum element should be a part of the candidate subset. There are 2 “gaps” (containing non-selected elements) around this minimum element. There are two possible cases from here:

编辑1:提出剩余证据-正如我们所说,可以通过矛盾来证明。 假设最小元素应该是候选子集的一部分。 此最小元素周围有2个“空白”(包含未选择的元素)。 这里有两种可能的情况:

a) At least one of those two “gaps” contains >1 non-selected elements. For simplicity, let’s understand this scenario with ABXC where X is the selected element (AB are placed on one side because that gap contains >1 element). What if you had chosen B instead of X? The solution can’t worsen because X is the minimum element, also the rest of the candidate subset remains unchanged.

a)这两个“空白”中的至少一个包含> 1个未选择的元素。 为简单起见,让我们用ABXC了解这种情况,其中X是选定的元素(AB位于一侧,因为该间隙包含> 1个元素)。 如果您选择B而不是X怎么办? 解决方案不会恶化,因为X是最小元素,其余的候选子集​​也保持不变。

b) Both “gaps” contains exactly one element. The funny thing is that in this case, the minimum element must be the last one to be picked up according to the picking strategy that we came up with (pick the one with > 1 gaps first). We should pick the max(A, B, X) and given that X is the minimum, it will never be chosen.

b)两个“空白”仅包含一个要素。 有趣的是,在这种情况下,最小元素必须是根据我们提出的选择策略选择的最后一个元素(首先选择间隔> 1的元素)。 我们应该选择max(A,B,X),并且鉴于X是最小值,则永远不会选择它。

So we have proved that selecting the minimum element is never required. So you can just open-up the array around the minimum element and solve the linear version of the problem to get the correct answer.

因此,我们证明了选择最小元素绝不是必需的。 因此,您可以打开围绕最小元素的数组并求解问题的线性版本以获得正确答案。

Here is an implementation in python:

这是python中的实现:

class Solution:

def __init__(self):
self.dp = {}

def solve_linear(self, slices, ind, to_choose):
if (ind, to_choose) not in self.dp:
N = len(slices)
if N <= ind or to_choose == 0:
return 0
result_choose = self.solve_linear(slices, ind + 2, to_choose - 1)
result_not_choose = self.solve_linear(slices, ind + 1, to_choose)
self.dp[(ind, to_choose)] = max(result_choose + slices[ind], result_not_choose)
return self.dp[(ind, to_choose)]

def maxSizeSlices(self, slices: List[int]) -> int:
min_elem = min(slices)
min_ind = 0
while slices[min_ind] != min_elem:
min_ind += 1
N = len(slices)
min_ind += 1
arr = [0 for _ in range(N)]
for i in range(N):
arr[i] = slices[(min_ind + i) % N]
return self.solve_linear(arr, 0, int(N/3))

翻译自: https://medium.com/@deepak.dpkgpt/pizza-with-3n-slices-b23d9d5aaba7

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值