📜  门| GATE-CS-2016(套装1)|第 44 题(1)

📅  最后修改于: 2023-12-03 15:12:43.249000             🧑  作者: Mango

题目描述

给你一个包含 n 个元素的整数序列 A 和一个整数 S。需要判断序列 A 是否可以分为两个不相交的非空连续子序列,使得它们的总和相等,并且在它们之间不存在与 S 相等的元素。

函数签名
def check_subsequence(A: List[int], S: int) -> bool:
    pass
参数说明
  • A: 一个包含 n 个元素的整数序列
  • S: 一个整数
返回值
  • True:A 可以分为两个不相交的非空连续子序列,使得它们的总和相等,并且在它们之间不存在与 S 相等的元素。
  • False: A 不能满足上述条件。
示例
示例1
Input: A = [1, 2, 2, 1], S = 2
Output: True
示例2
Input: A = [1, 2, 2, 1], S = 1
Output: False
提示
  • A 中的元素个数为正整数且不超过 10^5
  • A 中的元素均为范围在 [-10^4, 10^4] 之间的整数
  • S 的范围在 [-10^4, 10^4] 之间
思路

本题相当于求解序列 A 是否可以被分成两段,且每段的和相等,且两段之间不包括 S,可以转化成一个替换问题,将 A 中的所有 S 元素替换为 -S。

代码实现时,先检查 A 中的元素之和是否为奇数(如果为偶数,显然无法分成两段),如果为奇数直接返回 False。然后,将所有 S 替换为 -S,并在 A 头和尾各加一个 0,就转化成了一个背包问题,即在 A- 中找到一些数,使它们的和为 A-/2,其中 A-/2 表示 A- 的元素之和的一半(由于有两个 0,在计算时无需考虑这两个元素的影响)。如果可以找到这样的一些数,说明原问题有解,否则无解。

代码实现
from typing import List

def check_subsequence(A: List[int], S: int) -> bool:
    n = len(A)
    total = sum(A)
    if total % 2:
        return False
    target = total // 2
    # 将所有 S 替换为 -S
    for i in range(n):
        if A[i] == S:
            A[i] = -S
    # 在 A 头和尾各加一个 0,转化成背包问题
    A = [0] + A + [0]
    n += 2

    # dp[i][j] 表示前 i 个数,是否能恰好组成 j
    dp = [[False] * (target+1) for _ in range(n)]
    dp[0][0] = True
    for i in range(1, n):
        for j in range(target+1):
            if j - A[i] < 0:
                dp[i][j] = dp[i-1][j]
            else:
                dp[i][j] = dp[i-1][j] or dp[i-1][j-A[i]]
    return dp[n-1][target]
复杂度分析
  • 时间复杂度:O(n^2),其中 n 为序列 A 的长度。
  • 空间复杂度:O(n^2),与时间复杂度相同。