376. 摆动序列
定义前缀dp:前i个元素的最长 上升/下降 摆动序列
笔试题
一个整数序列,如果两个相邻元素的差恰好正负(负正)交替出现(差值不能为0),则该序列被称为摇摆序列,其相邻元素的差值绝对值的和为其差值总和。输入一个n个元素的序列(2<n<1000),输出其中差值总和最大的摇摆子序列。
例子:
输入:[1,2,3,4,3,2,1]
输出[:1,4,1]
因为从前面任意一个结尾转移都有可能,因此dp不能定义前缀形式,只能定义为以该元素为结尾的升/下降 摆动子序列的最大差值总和。
因为要输出子序列,因此记录当前状态是由前一个哪个状态转移而来,之后回溯得到所有子序列答案
稍加证明可以得知,最大的一定是以最后一个元素结尾。
nums = [1,2,3,4,3,2,1]
nums = [1,6,3,4,-2,4,1]
## 以i为结尾的上升/下降 差值总和最大的摆动子序列的差值总和,0上升,1下降
dp = [[0,0] for _ in range(len(nums))]
dp[0] = [0,0]
dppath = [[[-1], [-1]] for _ in range(len(nums))] ## 记录转移
maxsum = 0
res = []
lastsq = []
for i in range(1, len(nums)):
for j in range(i):
if nums[j] < nums[i]: ## 下降--> 上升
if dp[j][1]+(nums[i]-nums[j])**2 > dp[i][0]:
dppath[i][0] = [j]
elif dp[j][1]+(nums[i]-nums[j])**2 == dp[i][0]:
dppath[i][0].append(j)
dp[i][0] = max(dp[i][0], dp[j][1]+(nums[i]-nums[j])**2)
elif nums[j] > nums[i]: ## 上升--> 下降
if dp[j][0]+(nums[j]-nums[i])**2 > dp[i][1]:
dppath[i][1] = [j]
elif dp[j][0]+(nums[j]-nums[i])**2 == dp[i][1]:
dppath[i][1].append(j)
dp[i][1] = max(dp[i][1], dp[j][0]+(nums[j]-nums[i])**2)
### 回溯找路径
def dfs(last, path, state):
if last == [-1]:
res.append(path[::-1])
return
for num in last:
dfs(dppath[num][state], path+[nums[num]], not state)
### 最大值一定是最后一个
if dp[-1][0] > dp[-1][1]:
dfs(dppath[-1][0], [nums[-1]], state=1)
elif dp[-1][0] < dp[-1][1]:
dfs(dppath[-1][1], [nums[-1]], state=0)
else:
dfs(dppath[-1][0], [nums[-1]], state=1)
dfs(dppath[-1][1], [nums[-1]], state=0)
print(res)