每日一题: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):

java 复制代码
import java.util.Comparator;
import java.util.PriorityQueue;

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

    public Node(){}

    public Node(T data) {this.data = data;}
}
class Solution {
    public int maxSizeSlices(int[] slices) {
        PriorityQueue<Node<Integer>> pq = new PriorityQueue<>(new Comparator<Node<Integer>>() {
            @Override
            public int compare(Node<Integer> o1, Node<Integer> 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<n;i++) {
            nodes[i] = new Node(slices[i]);
            nodes[i].index = i;
            pq.add(nodes[i]);
        }
        for (int i=0;i<n;i++) {
            nodes[i].pre = nodes[(i-1+n)%n];
            nodes[i].next = nodes[(i+1)%n];
        }

        while (step < maxStep) {
            Node cur = pq.poll();
            if (!vis[cur.index]) {
                step += 1;
                ans += (int)cur.data;
                cur.data = (int)cur.pre.data + (int)cur.next.data - (int)cur.data;
                vis[cur.pre.index] = true;
                vis[cur.next.index] = true;
                // 这里需要注意,需要将前后节点进行删除。
                cur.pre = cur.pre.pre;
                cur.pre.next = cur;
                cur.next = cur.next.next;
                cur.next.pre = cur;
                pq.add(cur); // 后悔操作
//                System.out.println(step + " " + cur.index + " " + ans);
            }
        }
        return ans;
    }
}
相关推荐
深邃-1 小时前
【数据结构与算法】-二叉树(2):实现顺序结构二叉树(堆的实现),向上调整算法,向下调整算法,堆排序,TOP-K问题
数据结构·算法·二叉树·排序算法·堆排序··top-k
We་ct4 小时前
LeetCode 5. 最长回文子串:DP + 中心扩展
前端·javascript·算法·leetcode·typescript
王老师青少年编程8 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【哈夫曼贪心】:合并果子
c++·算法·贪心·csp·信奥赛·哈夫曼贪心·合并果子
叼烟扛炮8 小时前
C++第二讲:类和对象(上)
数据结构·c++·算法·类和对象·struct·实例化
天疆说9 小时前
【哈密顿力学】深入解读航天器交会最优控制中的Hamilton函数
人工智能·算法·机器学习
wuweijianlove9 小时前
关于算法设计中的代价函数优化与约束求解的技术7
算法
leoufung10 小时前
LeetCode 149: Max Points on a Line - 解题思路详解
算法·leetcode·职场和发展
样例过了就是过了10 小时前
LeetCode热题100 最长公共子序列
c++·算法·leetcode·动态规划
HXDGCL10 小时前
矩形环形导轨:自动化循环线的核心运动单元解析
运维·算法·自动化
谭欣辰10 小时前
C++ 排列组合完整指南
开发语言·c++·算法