和的逆运算问题(Python) | 豆包MarsCode AI刷题

115 阅读4分钟

问题描述

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 个数。
  • 创建结果字符串:将结果列表转换为字符串并返回。

至此,本题解答完毕。