每日一题:leetcode1338 3n块披萨

给你一个披萨,它由 3n 块不同大小的部分组成,现在你和你的朋友们需要按照如下规则来分披萨:

  • 你挑选 任意 一块披萨。
  • Alice 将会挑选你所选择的披萨逆时针方向的下一块披萨。
  • Bob 将会挑选你所选择的披萨顺时针方向的下一块披萨。
  • 重复上述过程直到没有披萨剩下。

每一块披萨的大小按顺时针方向由循环数组 slices 表示。

请你返回你可以获得的披萨大小总和的最大值。

输入:slices = [1,2,3,4,5,6]
输出:10
解释:选择大小为 4 的披萨,Alice 和 Bob 分别挑选大小为 3 和 5 的披萨。然后你选择大小为 6 的披萨,Alice 和 Bob 分别挑选大小为 2 和 1 的披萨。你获得的披萨总大小为 4 + 6 = 10 。
输入:slices = [8,9,8,6,1,1]
输出:16
解释:两轮都选大小为 8 的披萨。如果你选择大小为 9 的披萨,你的朋友们就会选择大小为 8 的披萨,这种情况下你的总和不是最大的。

提示:

  • 1 <= slices.length <= 500
  • slices.length % 3 == 0
  • 1 <= slices[i] <= 1000

思路:

首先,每一次选择都是可以自由选择披萨,但是选择完成之后,左右两边披萨则是不能选择,所以可以简化题目,看成在循环列表中,选取n/3个不连续的元素的最大值。

两种解法:1、类似于小偷偷家的动态规划  2、贪心+优先队列模拟取数

这里只介绍第2种方法(第1种有空补上。。)

这道题目中,直观想到的贪心策略是每一步选取最大的一块。但以[8,9,8,1,2,3]为例,如果我们第一步选取了9,剩下的元素就变成了[1,2,3],我们最大只能选择3,这样的总和就只有12,而显然选取两个8可以得到16的总和,是更优的。

如果我们可以反悔就好了。问题是,怎么反悔?在上面的例子中,我们第一步选9之后,如果直接删除两个8,那就失去了反悔的机会,因为后面再也不会处理到它们了。所以,我们需要删除两个8对应的节点,同时保留它们的信息。信息保留在哪里?只能是9所对应的节点。

我们在选取9之后,将左右两个节点删除,同时将9修改为8+8−9=7,这样我们后面仍然有机会选到这个7,也就相当于反悔了对9的选择,而去选择了左右两边的两个8。

重复这样的操作,直到选取了n/3个元素为止,我们就得到了需要的最优解。

为什么我们的反悔操作一定是同时选择左右两个元素呢?因为我们是从大到小处理所有元素的,所以左右两边的元素一定不大于中间的元素,如果我们只选取其中的一个,是不可能得到更优解的。

ac code O(nlogn):


import java.util.Comparator;
import java.util.PriorityQueue;

public class Node {
    public T data;
    public int index;
    public Node pre;
    public Node next;

    public Node(){}

    public Node(T data) {this.data = data;}
}
class Solution {
    public int maxSizeSlices(int[] slices) {
        PriorityQueue> pq = new PriorityQueue<>(new Comparator>() {
            @Override
            public int compare(Node o1, Node o2) {
                return o2.data - o1.data; // 从大到小进行排序
            }
        });
        int n = slices.length;
        Node[] nodes = new Node[n];
        int step = 0;
        int maxStep = n / 3;
        int ans = 0;
        boolean[] vis = new boolean[n];
        for (int i=0;i

你可能感兴趣的:(LeetCode每日一题,算法)