【摆动序列】合集

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)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值