LeetCode 1388. Pizza With 3n Slices(3n 块披萨)(DP)

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

你挑选 任意 一块披萨。
Alice 将会挑选你所选择的披萨逆时针方向的下一块披萨。
Bob 将会挑选你所选择的披萨顺时针方向的下一块披萨。
重复上述过程直到没有披萨剩下。
每一块披萨的大小按顺时针方向由循环数组 slices 表示。

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

示例 1:

输入:slices = [1,2,3,4,5,6]
输出:10
解释:选择大小为 4 的披萨,Alice 和 Bob 分别挑选大小为 3 和 5 的披萨。然后你选择大小为 6 的披萨,Alice 和 Bob 分别挑选大小为 2 和 1 的披萨。你获得的披萨总大小为 4 + 6 = 10 。

这条比较难的一点是论证:

题目可以转换为,取任意 n/3 个不相邻的数字的最大和。

1、首先显然相邻的两个数字是不可能同时取到的。

2、任意长度为 n/3 的不相邻的子序列都可以被取到。

  尝试简单论证一下,

  用0表示不选择,1表示选择,当选择不相邻的n/3个数字之后,001001001 这样的序列可以表示为一种选取方法。

  显然,存在某个1的周围有至少三个0,如 0010 或 0100 (由于是一个圆 首尾时相连接的)

  因为如果不存在的话,则序列只能为 010101... 显然 1 的个数会大于 n/3

  然后先选取这样的 1 和周围的两个 0 ,那么,剩下还是会有一个 0,(不会出现两个1相邻)

  则剩下的序列还是满足最开始的条件,继续以同样的规则选择直到结束最后剩下010。

  举例说明:

  001010100

  先取第一个1和旁边的0 变为 010100

  取后面的1和旁边的0 变为 010 最后全部取走。

如果能想到上面的结论,剩下的就只是一个简单的DP了,DP[i][j] 表示取i块,最后一块序列为j的最大值,由于第一块和最后一块不能同时取,所以需要计算两次。其实和这道题蛮像的 https://leetcode-cn.com/problems/house-robber-ii/

class Solution {
public:
    int maxSizeSlices(vector<int>& slices) {
        int n = slices.size();
        int select = n / 3;
        int dp1[select + 1][n];
        int dp2[select + 1][n];
        memset(dp1, 0, sizeof dp1);
        memset(dp2, 0, sizeof dp2);
        dp1[1][0] = slices[0];
        dp2[1][1] = slices[1];
        for (int i = 1; i <= select; i++) {
            for (int j = 2; j < n; j++) {
                for (int k = 0; k < j - 1; k++) {
                    dp1[i][j] = max(dp1[i][j], dp1[i-1][k] + slices[j]);
                    dp2[i][j] = max(dp2[i][j], dp2[i-1][k] + slices[j]);
                }
            }
        }
        int ans = 0;
        for (int i = 0; i < n - 1; i++) {
            ans = max(ans, dp1[select][i]);
        }
        for (int i = 0; i < n; i++) {
            ans = max(ans, dp2[select][i]);
        }
        return ans;
    }
};

三层循环可以简化为两层循环

class Solution {
public:
    int maxSizeSlices(vector<int>& slices) {
        int n = slices.size();
        int select = n / 3;
        int dp1[select + 1][n];
        int dp2[select + 1][n];
        memset(dp1, 0, sizeof dp1);
        memset(dp2, 0, sizeof dp2);
        dp1[1][0] = slices[0];
        dp2[1][1] = slices[1];
        int max1, max2;
        for (int i = 1; i <= select; i++) {
            max1 = dp1[i-1][0];
            max2 = dp2[i-1][0];
            for (int j = 2; j < n; j++) {
                dp1[i][j] = max1 + slices[j];
                dp2[i][j] = max2 + slices[j];
                max1 = max(max1, dp1[i-1][j-1]);
                max2 = max(max2, dp2[i-1][j-1]);
            }
        }
        int ans = 0;
        for (int i = 0; i < n - 1; i++) {
            ans = max(ans, dp1[select][i]);
        }
        for (int i = 0; i < n; i++) {
            ans = max(ans, dp2[select][i]);
        }
        return ans;
    }
};

原文地址:https://www.cnblogs.com/wenruo/p/12563420.html

时间: 2024-10-04 03:58:20

LeetCode 1388. Pizza With 3n Slices(3n 块披萨)(DP)的相关文章

leetcode 413. 等差数列划分(Arithmetic Slices)

目录 题目描述: 示例: 解法: 题目描述: 如果一个数列至少有三个元素,并且任意两个相邻元素之差相同,则称该数列为等差数列. 例如,以下数列为等差数列: 1, 3, 5, 7, 9 7, 7, 7, 7 3, -1, -5, -9 以下数列不是等差数列. 1, 1, 2, 5, 7 数组 A 包含 N 个数,且索引从0开始.数组 A 的一个子数组划分为数组 (P, Q),P 与 Q 是整数且满足 0<=P<Q<N . 如果满足以下条件,则称子数组(P, Q)为等差数组: 元素 A[P]

LeetCode—House Robber 寻找数组不相邻组合最大值DP

https://leetcode.com/problems/house-robber/ 题目设计了一个抢劫犯的情景,其实就是求数组中不相邻数据进行组合得到的最大值 举一个例子 假设数据: 8 3 6 15 4 9 7 10 那么首先可能选取 8 , 3 每一个数字的选取都是根据他的前两个数字,前三个数字得到的最大值进行选择,等到6的时候考虑前面只能和8组合  8,3,14 到数字15,那么就可以考虑在8,3中进行组合  8,3,14,23,4,9,7,10 接下来的步骤: 8,3,14,23,1

leetcode 201. Bitwise AND of Numbers Range(位运算,dp)

Given a range [m, n] where 0 <= m <= n <= 2147483647, return the bitwise AND of all numbers in this range, inclusive. For example, given the range [5, 7], you should return 4. 题解:如果m==n,那么答案就是m. 如果m<n,那么二进制最右边一位在最后的结果中肯定是0,那么就可以转化成子问题: rangeBi

第十六周 Leetcode 600. Non-negative Integers without Consecutive Ones(HARD) 计数dp

Leetcode600 很简单的一道计数题 给定整数n 求不大于n的正整数中 二进制表示没有连续的1的数字个数 在dp过程中只要保证不出现连续1以及大于n的情况即可. 所以设计按位dp[i][j]表示到第i位 j=0表示第i位为0 且值等于n的情况 2为值小于n的情况 j=1表示第i位为1 且值等于n的情况 3为值小于n的情况 转移方程很简单 看代码吧 这道题应该是Mid难度吧 class Solution { public: int findIntegers(int num) { int va

Leetcode 详解(股票交易日)(动态规划DP)

问题描述: 在股市的交易日中,假设最多可进行两次买卖(即买和卖的次数均小于等于2),规则是必须一笔成交后进行另一笔(即买-卖-买-卖的顺序进行).给出一天中的股票变化序列,请写一个程序计算一天可以获得的最大收益.请采用实践复杂度低的方法实现. 给定价格序列prices及它的长度n,请返回最大收益.保证长度小于等于500. class Solution { public: int maxProfit(vector<int>& prices) { //It's wrong if you c

LeetCode 1223. 掷骰子模拟 Dice Roll Simulation - Java - DP

题目链接:1223. 掷骰子模拟 有一个骰子模拟器会每次投掷的时候生成一个 1 到 6 的随机数. 不过我们在使用它时有个约束,就是使得投掷骰子时,连续 掷出数字 i 的次数不能超过 rollMax[i](i 从 1 开始编号). 现在,给你一个整数数组 rollMax 和一个整数 n,请你来计算掷 n 次骰子可得到的不同点数序列的数量. 假如两个序列中至少存在一个元素不同,就认为这两个序列是不同的.由于答案可能很大,所以请返回 模 \(10^9 + 7\) 之后的结果. 示例1: 输入:n =

LeetCode 1269. 停在原地的方案数 (DP)

题目 有一个长度为 arrLen 的数组,开始有一个指针在索引 0 处. 每一步操作中,你可以将指针向左或向右移动 1 步,或者停在原地(指针不能被移动到数组范围外). 给你两个整数 steps 和 arrLen ,请你计算并返回:在恰好执行 steps 次操作以后,指针仍然指向索引 0 处的方案数. 由于答案可能会很大,请返回方案数 模 10^9 + 7 后的结果. 示例1: 输入:steps = 3, arrLen = 2 输出:4 解释:3 步后,总共有 4 种不同的方法可以停在索引 0

LeetCode 136、137:Single Number I &amp; II

Single Number Given an array of integers, every element appears twice except for one. Find that single one. Note: Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory? Single Number II Given an arr

LeetCode 137:Single Number II

Given an array of integers, every element appears three times except for one. Find that single one. Note: Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory? Single Number II 比Single Number要复杂的多,