问题重述
你有一个长度为 N 的数组 A,和一个整数 X。你可以进行以下操作:选择两个相邻的元素 A[i] 和 A[i+1],如果它们都小于 X,则可以将它们合并成它们的和,并替换原来的两个元素。
例如,如果数组是 [2, 6, 1, 9] 且 X = 5,你可以选择 i = 1,合并 6 和 1,得到新数组 [2, 7, 9]。
你的任务是判断是否可以通过多次这样的操作,将数组 A 缩减到只剩下一个元素。如果能,返回 1,否则返回 0。
示例
样例1:
输入:
N = 3, X = 5, A = [4, 3, 1]
输出:1
解释:可以先合并3和1,得到[4, 4],然后再合并4和4,得到[8]。
样例2:
输入:
N = 4, X = 10, A = [7, 2, 5, 1]
输出:1
解释:可以先合并2和5,得到[7, 7, 1],然后再合并7和1,得到[8, 7],最后合并8和7,得到[15]。
样例3:
输入:
N = 5, X = 8, A = [3, 3, 2, 2, 1]
输出:1
解释:可以先合并3和3,得到[6, 2, 2, 1],然后再合并2和2,得到[6, 4, 1],最后合并4和1,得到[6, 5],再合并6和5,得到[11]。
问题分析
这个问题基于遍历的思想很好解决,这里包含两个要点:我们需要判断是否可以通过一系列操作将数组 A 缩减到长度为 1。其二,每次操作可以选择两个相邻且都小于 X 的元素,将它们合并成它们的和。如果是非相邻的遍历,可能首先想到的是双指针法,但在相邻的遍历中,使用回溯构造遍历二叉树的方式似乎更合适(PS:其实和数组总和那道题目很像)。我们只需要按照这棵树尝试所有可能即可,即当前这一步能合并的基础上讨论下一步能不能继续合并。
示例代码
def solution(N: int, X: int, A: list) -> int:
# write code here
# 回溯法
def dfs(_list, idx):
# 合并到最后只剩俩
if len(_list) == 2 and _list[0] < X and _list[1] < X:
return 1
# 坐标已经不能往右边移动了
if idx >= len(_list)-1:
return 0
# 大于三的
if _list[idx] < X and _list[idx+1] < X:
# 如果能先合并,那就合并了再从头移动
new_list = _list[:idx]+[_list[idx]+_list[idx+1]]
if idx+2 < len(_list):
new_list += _list[idx+2:]
if dfs(new_list,0) == 0:
return dfs(_list,idx+1)
else:
return 1
else:
return dfs(_list,idx+1)
return dfs(A,0)
if __name__ == '__main__':
print(solution(N=3, X=5, A=[4, 3, 1]) == 1)
print(solution(N=4, X=10, A=[7, 2, 5, 1]) == 1)
print(solution(N=5, X=8, A=[3, 3, 2, 2, 1]) == 1)
代码分析
定义一个递归函数 dfs,参数为当前数组 _list 和当前索引 idx。合并之后的list作为参数进一步传递到下一级处理中,同时idx用来标记当前的操作位置(如果合并成功了,则从头开始,否则每次都会移动至下一位)。如果当前元素和下一个元素都小于 X,则进行合并操作,并继续从当前索引开始递归。
否则,继续向右移动索引。在遇到两者情况之一终止递归:
- 如果数组长度为
2且两个元素都小于X,返回1。 - 如果索引
idx已经不能往右边移动,返回0。