Skip to content

M26976: 摆动序列

greedy, dp, http://cs101.openjudge.cn/practice/26976/

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。

  • 例如,[1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。

  • 相反,[1, 4, 7, 2, 5],[1, 7, 4, 5, 5],不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。

给你一个整数数组 nums ,返回 nums 中作为 摆动序列最长子序列的长度

输入

第一行包含一个整数n。1 <= n <= 1000

第二行包含n个整数,相邻整数间以空格隔开。0 <= nums[i] <= 1000

输出

一个整数

样例输入

sample1 input:
6
1 7 4 9 2 5
sample1 output:
6

sample2 input:
10
1 17 5 10 13 15 10 5 16 8
sample2 output:
7

样例输出

sample3 input:
9
1 2 3 4 5 6 7 8 9
sample3 output:
2

提示

tags: greedy

来源: LeetCode 376. 摆动序列: https://leetcode.cn/problems/wiggle-subsequence/

与 02181: Jumping Cows 一样的方法。

一个一个比较过去,摆动了就计数,不摆就跳过(记得处理==0情况)

python
# 23n2300010763, 胡睿诚

def sgn(x):
    if x == 0:
        return 0
    elif x > 0:
        return 1
    elif x < 0:
        return -1


n = int(input())
nums = list(map(int,input().split()))
delta = [sgn(nums[i+1]-nums[i]) for i in range(n-1)]

result = 1
pre = 0
for i in range(n-1):
    if delta[i] * pre < 0 or (pre == 0 and delta[i] != 0):
        result += 1
        pre = delta[i]
print(result)

dp

python
# 高景行 24数学科学学院
n = int(input())
a = list(map(int, input().split()))
dp = [[1, 1] for _ in range(n)]
# dp[i][0] 最长摆动序列长度 (最后一个 < 上一个)
# dp[i][1] 最长摆动序列长度 (最后一个 > 上一个)
ans = 1
for i in range(1, n):
    if a[i] < a[i - 1]:
        dp[i][0] = dp[i - 1][1] + 1
        dp[i][1] = dp[i - 1][1]
    elif a[i] > a[i - 1]:
        dp[i][1] = dp[i - 1][0] + 1
        dp[i][0] = dp[i - 1][0]
    else:
        dp[i][1] = dp[i - 1][1]
        dp[i][0] = dp[i - 1][0]
print(max(dp[n - 1][0], dp[n - 1][1]))
python
def wiggleMaxLength(nums):
    n = len(nums)
    if n < 2:
        return n
    
    prevdiff = nums[1] - nums[0]
    ret = (2 if prevdiff != 0 else 1)
    for i in range(2, n):
        diff = nums[i] - nums[i - 1]
        if (diff > 0 and prevdiff <= 0) or (diff < 0 and prevdiff >= 0):
            ret += 1
            prevdiff = diff
    
    return ret

input()
*nums, = map(int, input().split())
ans = wiggleMaxLength(nums)
print(ans)
python
# 苦行僧, 搞那么复杂干嘛,又波峰又波谷,贪心,动规,完全没必要,统计变化次数就好了
def wiggleMaxLength(nums):
    n = len(nums)
    if n == 1:
        return 1

    direction = None

    res = 0 
    
    for i in range(1, n):
        if nums[i] == nums[i-1]:  # 无变化
            continue
        
        # 有变化:升高了
        elif nums[i] - nums[i-1] > 0:
            # 如果上一次也是升高,不要算进去,因为其实不是摆动
            if direction == 1:
                continue
            
            direction = 1
            res += 1
        
        # 有变化:降低了
        else:
            # 如果上一次也是降低,不要算进去,因为其实不是摆动
            if direction == 0:
                continue
            
            direction = 0
            res += 1
    

    return res+1 # 因为统计的是变化的次数,最终的结果是序列的长度,所以需要+1.


input()
*nums, = map(int, input().split())
ans = wiggleMaxLength(nums)
print(ans)

【薛之瑶 生科学院】思路:考虑转折次数。开一个旗帜变量记录上一次是向上还是向下。如果发生转折就答案+1。最后的序列长度即为转折次数+1.注意开头结尾,以及如果中间一段是平的也算一次转折。

python
n=int(input())  
nums=list(map(int,input().split()))  
dir=0  
ans=0  
for i in range(1,n):  
    if(nums[i]>nums[i-1]):  
        if(dir==1):  
            continue  
        ans+=1  
        dir=1  
    elif(nums[i]<nums[i-1]):  
        if(dir==-1):  
            continue  
        ans+=1  
        dir=-1  
print(ans+1)

dp

python
n = int(input())
li = list(map(int, input().split()))
dpup = [1] * n
dpdo = [1] * n
for i in range(1, n):
    if li[i] > li[i-1]:
        dpup[i] = max(dpdo[i-1] + 1, dpup[i-1])
        dpdo[i] = dpdo[i-1]
    elif li[i] < li[i-1]:
        dpdo[i] = max(dpup[i-1] + 1, dpdo[i-1])
        dpup[i] = dpup[i-1]
    else:
        dpup[i] = dpup[i-1]
        dpdo[i] = dpdo[i-1]

print(max(dpup[-1], dpdo[-1]))

对原始的 动态规划(DP)解法 进行空间优化,将空间复杂度从 O(n) 降到 O(1)


✅ 原始问题回顾

我们要找一个序列的最长摆动子序列长度(元素交替上升和下降)。

原始方法用了两个数组:

  • dpup[i]:以第 i 个元素结尾、最后是“上升”的最长摆动序列长度
  • dpdo[i]:以第 i 个元素结尾、最后是“下降”的最长摆动序列长度

但实际上,我们只关心前一个状态,所以可以用两个变量代替整个数组。


🚀 优化版本(空间 O(1))

python
n = int(input())
li = list(map(int, input().split()))

# 初始化:单个元素,长度为1,上升和下降都算1
dpup = 1   # 当前结尾为“上升”的最长摆动序列长度
dpdo = 1   # 当前结尾为“下降”的最长摆动序列长度

for i in range(1, n):
    if li[i] > li[i-1]:
        dpup = max(dpdo + 1, dpup)   # 上升:接在下降后面
        # dpdo 不变
    elif li[i] < li[i-1]:
        dpdo = max(dpup + 1, dpdo)   # 下降:接在上升后面
        # dpup 不变
    # 如果相等,两者都不变

print(max(dpup, dpdo))