【LeetCode 算法】Pizza With 3n Slices 3n 块披萨 -记忆化

Pizza With 3n Slices 3n 块披萨

问题描述:

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

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

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

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

1 < = s l i c e s . l e n g t h < = 500 s l i c e s . l e n g t h 1 < = s l i c e s [ i ] < = 1000 1 <= slices.length <= 500\\ slices.length % 3 == 0\\ 1 <= slices[i] <= 1000 1<=slices.length<=500slices.length1<=slices[i]<=1000

分析

今天的pizz 比较hard。

这个圆形的pizza,每一块都有编号,而且每一块的大小不一样。 这里用了一个一维数组来表示这个pizza。

步骤

它要求每次user可以先选一块,然后AB会选user选择的相邻的2块。
每次操作就会减少3块,一共可以进行n次。
要计算出user可以选择的pizza的总和最大。

也就是说一定会存在某个方法,可以使user的在n次操作后,可以得到的pizza大小累加最大。

一般这样,都会想到用递归枚举来做,如果按照昨天的每日pizza问题的递归思路是无法解决的,具体的可以尝试。

转换思路

这个问题,还是比较难想的,如果之前有见过 house robber问题的,可能比较容易理解。

该问题对应的模型,其实近似于在前3n个数中,选择n个不相邻的数,可以得到的最大和

也就是说,所有方案中user选择的数字一定都是间隔的,特别需要强调的是这里是环形的,所以需要处理首尾的2个元素被选中的方案

思路

到此,需要解决的就是如何在前i个数字中选出j个不相邻的元素的最大值
使用 d f s ( i , j ) dfs(i,j) dfs(i,j)来表示这个递归。
其子问题可以分解为,

  • 第i个数字选择 的情况下,那么第i-1一定是不能选的,即
    d f s ( i , j ) = d f s ( i − 2 , j − 1 ) + a [ i ] dfs(i,j) = dfs(i-2,j-1)+ a[i] dfs(i,j)=dfs(i2,j1)+a[i]
  • 另一个情况就是第i个数字不选,
    d f s ( i , j ) = d f s ( i − 1 , j ) dfs(i,j) = dfs(i-1,j) dfs(i,j)=dfs(i1,j)

所以 d f s ( i , j ) = m a x ( d f s ( i − 2 , j − 1 ) + a [ i ] , d f s ( i − 1 , j ) ) dfs(i,j) = max( dfs(i-2,j-1)+ a[i],dfs(i-1,j)) dfs(i,j)=max(dfs(i2,j1)+a[i],dfs(i1,j))
到此基于非环形的pizza,就可以了,这里会出现一个比较特殊的情况,即第0个被选中,第n-1个也被选中时的得分最大,也是符合这个递归要求的

但是这个情况并不符合问题的要求,在环形的情况下2个元素相邻了

为了避免这个问题,做一下调节即可,先计算 0 → n − 2 0\rightarrow n-2 0n2 区间的最大和,此时一定不会出现首尾相邻,然后计算 1 → n − 1 1\rightarrow n-1 1n1,此时也是符合要求的。

到此整个问题就可以AC了。

代码

递归

class Solution {
    
    int INF;
    int[][] memo;    
    public int maxSizeSlices(int[] slices) {
        int n = slices.length;
        int[] s1 = new int[n-1];//0~n-2
        int[] s2 = new int[n-1];//1~n-1
        for(int i= 1;i<n;i++){
            s1[i-1] = slices[i-1];
            s2[i-1] = slices[i];
        }
        INF = Integer.MIN_VALUE/2;
        int cnt = n/3;
        memo = new int[n][cnt+1];
        for(int i = 0;i<n;i++){
            Arrays.fill(memo[i],-1);
        }   
        int res = dfs(n-1,cnt,s1);
        for(int i = 0;i<n;i++){
            Arrays.fill(memo[i],-1);
        }
        res = Math.max(res,dfs(n-1,cnt,s2));
        return res;
    }
    public int dfs(int idx,int cnt,int[] s){
        if(memo[idx][cnt]!=-1){
            return memo[idx][cnt];
        }
        if(cnt==0||idx<3){
            //cnt==0 idx = any
            if(cnt==0) return 0;
            // cnt ==any  idx<3
            //cnt==1 idx <3
            if(cnt==1){
                return idx==1?s[0]:Math.max(s[0],s[1]);
            }
            // cnt >1  idx<3            
            return memo[idx][cnt] = INF;
        }
        // idx>=3 && cnt>0
          
        int res =dfs(idx-2,cnt-1,s)+s[idx-1];
        res = Math.max(res,dfs(idx-1,cnt,s));
        return memo[idx][cnt] = res;
    }
}

时间复杂度 O ( N 2 ) O(N^2) O(N2)

空间复杂度 O ( N 2 ) O(N^2) O(N2)

Tag

Array

Memoization

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eric.Cui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值