GO语言算法实战2 | 青训营
作者:LoHhhha
时间:2023.8.19
题目
给你一个披萨,它由 3n 块不同大小的部分组成,现在你和你的朋友们需要按照如下规则来分披萨:
- 你挑选 任意 一块披萨。
- Alice 将会挑选你所选择的披萨逆时针方向的下一块披萨。
- Bob 将会挑选你所选择的披萨顺时针方向的下一块披萨。
- 重复上述过程直到没有披萨剩下。
每一块披萨的大小按顺时针方向由循环数组 slices 表示。
请你返回你可以获得的披萨大小总和的最大值。
1 <= slices.length <= 500slices.length % 3 == 01 <= slices[i] <= 1000
思路
对于本题,关键词为:动态规划、分类讨论
我们可以很轻易的看到以下几个性质:
- 我们不可能挑选相邻的两块披萨(包括第一块和最后一块)
- 我们只能且必须选择其中的
n/3块披萨
我们该如何找出满足选择n/3块披萨的最佳可能?
我们很轻易想到可以用类似01背包的思想,对于每一个状态,我们维护这个状态的维护位置、剩余选择。
但我们很快发现一个问题:由于我们需要从不相邻的元素完成迁移,该怎么去做状态迁移?
观察dp[idx][k]的性质:维护到下标idx,选择了k块披萨的最优情况。这导致我们可以跳跃地维护我们的状态,在维护状态dp[idx][k]时,若选择idx下标处的披萨,我们只能去考虑dp[idx-2][k-1]的最优,因为我们不能再选择前一块披萨;而不选择idx下标处的披萨,我们可以选择前一块披萨,所以我们可以直接考虑dp[idx-1][k]作为当前状态的转移(注意:idx-1下标处的披萨在dp[idx-1][k]状态并不是一定选择的)。
所以有如下转移方程:
dp[i][j]=max(dp[i-2][j-1]+part[i],dp[i-1][j])
但我们还有一个问题需要考虑:我们怎么保证头尾不同时被选?
有一个简单易行的方法:将原问题分为两个问题。第一个问题是原数组剔除尾部,第二个问题是原数组剔除头部。这样可以简单易行地让头与尾不同时被选择,取两个中的最大值作为答案。
代码实现
func max(a int, b int) int {
if a>b{
return a
}
return b
}
func getRes(slices []int,start int, end int) int{
n:=end-start+1;
m:=len(slices)/3
dp:=make([][]int,n+1)
for i:=0;i<=n;i++{
dp[i]=make([]int,m+1)
}
// dp[i][j]=max(dp[i-2][j-1]+slices[i],dp[i-1][j])
dp[1][1]=slices[start]
for i:=2;i<=n;i++{
for j:=0;j<=m;j++{
if j>=1{
dp[i][j]=max(dp[i-2][j-1]+slices[start+i-1],dp[i-1][j])
} else{
dp[i][j]=dp[i-1][j]
}
}
}
return dp[n][m]
}
func maxSizeSlices(slices []int) int {
n:=len(slices)
return max(getRes(slices,0,n-2),getRes(slices,1,n-1))
}