问题描述
n 个整数两两相加可以得到 n(n - 1) / 2 个和。我们的目标是:根据这些和找出原来的 n 个整数。
按非降序排序返回这 n 个数,如果无解,输出 "Impossible"。
测试样例
样例1:
输入:
n = 3, sums = [1269, 1160, 1663]
输出:"383 777 886"
样例2:
输入:
n = 3, sums = [1, 1, 1]
输出:"Impossible"
样例3:
输入:
n = 5, sums = [226, 223, 225, 224, 227, 229, 228, 226, 225, 227]
输出:"111 112 113 114 115"
样例4:
输入:
n = 5, sums = [-1, 0, -1, -2, 1, 0, -1, 1, 0, -1]
输出:"-1 -1 0 0 1"
样例5:
输入:
n = 5, sums = [79950, 79936, 79942, 79962, 79954, 79972, 79960, 79968, 79924, 79932]
输出:"39953 39971 39979 39983 39989"
本题思路较为复杂,需要发掘问题中的隐含性质,且使用了遍历的思想,在此先给出代码如下:
def solution(n, sums):
total_sum = sum(sums)
# 检查综合是否能被(n-1)整除
if total_sum % (n - 1) != 0:
return "Impossible"
#创建并初始化数组,对其进行排序,方便后续操作
ans = [0] * n
sums.sort()
for i in range(-abs(sums[0]), abs(sums[0]) + 1):
ans[0] = i
p = {s: sums.count(s) for s in sums} # 创建一个频率映射,计算每个和出现的次数
idx = 1
for k in p.keys():
if p[k] == 0:
continue
while p[k] > 0:
ans[idx] = k - ans[0]
# 检查有多少个之前的索引可以与 ans[idx] 形成和
t = 0
while t < idx:
# 如果组合存在,减少其计数
if p.get(ans[idx] + ans[t], 0):
p[ans[idx] + ans[t]] -= 1
else:
break
t += 1
# 果所有之前的对都有效
if t == idx:
idx += 1
else:
break
# 如果成功填充了所有n个数,则跳出循环。
if idx == n:
break
# 创建结果字符串,返回结果
return " ".join(map(str, ans[:-1])) + " " + str(ans[-1])
if __name__ == "__main__":
print(solution(3, [1269, 1160, 1663]) == "383 777 886")
print(solution(3, [1, 1, 1]) == "Impossible")
print(solution(5, [226, 223, 225, 224, 227, 229, 228, 226, 225, 227]) == "111 112 113 114 115")
print(solution(5, [-1, 0, -1, -2, 1, 0, -1, 1, 0, -1]) == "-1 -1 0 0 1")
print(solution(5, [79950, 79936, 79942, 79962, 79954, 79972, 79960, 79968, 79924, 79932]) == "39953 39971 39979 39983 39989")
代码思路分析
1. 总和检查
total_sum = sum(sums)
# 检查总和是否能被 (n-1) 整除
if total_sum % (n - 1) != 0:
return "Impossible"
- 目的:首先计算所有和的总和
total_sum,并检查它是否能被(n-1)整除。如果不能整除,说明无法找到满足条件的n个整数,直接返回"Impossible"。
2. 初始化
ans = [0] * n
sums.sort()
- 目的:初始化一个长度为
n的结果列表ans,并对和列表sums进行排序。排序是为了方便后续处理。
3. 遍历可能的最小值
for i in range(-abs(sums[0]), abs(sums[0]) + 1):
ans[0] = i
p = {s: sums.count(s) for s in sums} # 创建一个频率映射
- 目的:遍历可能的最小值
i,从-abs(sums[0])到abs(sums[0])。将i赋值给ans[0],并创建一个频率映射p,用于记录和列表中每个和的出现次数。
4. 填充结果列表
idx = 1
for k in p.keys():
if p[k] == 0:
continue
while p[k] > 0:
ans[idx] = k - ans[0]
# 检查有多少个之前的索引可以与 ans[idx] 形成和
t = 0
while t < idx:
# 如果组合存在,减少其计数
if p.get(ans[idx] + ans[t], 0):
p[ans[idx] + ans[t]] -= 1
else:
break
t += 1
# 如果所有之前的对都有效
if t == idx:
idx += 1
else:
break
- 目的:从频率映射
p中取出和k,尝试填充结果列表ans。对于每个和k,尝试找到一个数ans[idx],使得k - ans[0]是一个有效的数,并且与之前的数对和ans[idx] + ans[t]存在于p中。如果所有之前的对都有效,则继续填充下一个数;否则,跳出当前循环。
5. 验证结果
if idx == n:
break
- 目的:如果成功填充了所有
n个数,则跳出循环。
6. 创建结果字符串
return " ".join(map(str, ans[:-1])) + " " + str(ans[-1])
- 目的:将结果列表
ans转换为字符串,并返回结果。
总结
- 总和检查:确保总和能被
(n-1)整除。 - 初始化:初始化结果列表并排序和列表。
- 遍历可能的最小值:尝试不同的最小值
i。 - 填充结果列表:通过频率映射
p尝试填充结果列表ans。 - 验证结果:检查是否成功填充所有
n个数。 - 创建结果字符串:将结果列表转换为字符串并返回。
至此,本题解答完毕。