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

130 阅读2分钟

1 题目描述

n 个整数两两相加可以得到 n(n - 1) / 2个和。我们的目标是:根据这些和找出原来的 n 个整数。按非降序排序返回这 n 个数,如果无解,输出 "Impossible"。 图片.png

2 解题思路

首先看到这题难度,小小中等,这还不简简单单拿下。仔细一看,好像不太对劲,题目的字都认识,读完咋完全没思路。这时候没思路也不能干瞪眼啊,试试这个豆包新出的AI功能,只需要点击右侧的AI->需要一点思路提示。

图片.png

我生成的提示如下

图片.png

看上去马马虎虎,我自己多想一会儿也能想到。总结下来就是两个最小的之和在两两相加的和当中也是最小的,这样自然会想到先给这些和排序,不管那么多,先上个sort。
sums.sort() 

然后假设现在对n个数求解,可以先设这n个数为:a1a2a3......ana_1、a_2、a_3 ...... a_n,并假设他们是按非递减排列的;假设x1x2x3......x(n+1)n/2x_1、x_2、x_3......x_{(n+1)*n/2}是它们两两求和,而且也是非递减排序。这样我们就可以看出一些对应关系:

a1+a2=x1a_1 + a_2 = x_1

因为a1+a3<a2+a3a_1 + a_3 < a_2 + a_3,所以可以想到第二小的x2x_2为:

a1+a3=x2a_1 + a_3 = x_2

现在知道了两个等式,却有三个未知数,要是我们想求解,那么必须知道a2+a3a_2 + a_3的值,显然我们并不能确定它,那么不妨假设我们知道,并假设它是:xyx_y,我们知道两两求和,最多有n-1个和是由a1a_1和其他值加和得来,而如果排除带有a1a_1的和,那么a2+a3a_2 + a_3将是最小的。也就能推断出,y的值最大为n,我们可以得到

2<y<n+12 < y < n+1

那么现在我们就来开开心心地求出a1,a2,a3a_1 , a_2 , a_3

a1+a2=x1a1+a3=x2a2+a3=xy2<y<n+1a_1 + a_2 = x_1\\ a_1 + a_3 = x_2\\ a_2 + a_3 = x_y\\ 2 < y < n+1

对它求解有:

a1=(x1+x2xy)/2a2=(x1+xyx2)/2a3=(x2+xyx1)/22<y<n+1a_1 = (x_1 + x_2 - x_y) / 2\\ a_2 = (x_1 + x_y - x_2) / 2\\ a_3 = (x_2 + x_y - x_1) / 2\\ 2 < y < n+1

再来看看求得这些后有什么好处,首先我们可以排除x列表中的一些已知元素,先假设y=3y=3这里就可以排除掉x1,x2,x3x_1,x_2,x_3,排除之后我们可以想到x中最小的值一定为a1+a4a_1 + a_4,因为下标比4小的a值的两两组合已经被排除掉了。这样我们可以求得a_4的具体的值。然后在x中再排除掉a1+a4,a2+a4,a3+a4a_1 + a_4,a_2 + a_4,a_3 + a_4。重复这个过程就可以求出所有的a值了。要是不行,那就改变y的值,直到所有的y值都试过后还是不能求出,那就返回“Impossible”。下面是具体的代码:

def solution(n, sums):
    # 对和列表进行排序
    sums.sort() 
    # 初始化结果列表
    result = []
    flag_used = [False]*len(sums)

    # 递归推导整数
    def deduce_numbers(sums, result, flag_used):
        # 如果已经找到 n 个整数,返回结果
        if len(result) == n:
            return result
        # 找到最小的和
        index = -1
        for i, item in enumerate(flag_used):
            if not item:
                index = i
                break
        if index == -1:
            return result
        result.append(sums[index] - result[0])
        #去掉已知最小和
        for x in result[:-1]:
            flag_charge = 1
            for i, item in enumerate(flag_used):
                if not item and x + result[-1] == sums[i]:
                    flag_used[i] = True; flag_charge = 0
                    break
            if flag_charge:
                return result
        return deduce_numbers(sums, result, flag_used)
    
    #对a_3的值进行假设
    min_sum_0 = sums[0]; min_sum_1 = sums[1]; flag_used[:2] = [True, True]
    for i in range(2, n):
        min_sum_2 = sums[i]; flag_used[i] = True
        if (min_sum_0 + min_sum_1 + min_sum_2) % 2 == 1:
            continue
        result.append(int((min_sum_0 + min_sum_1 - min_sum_2) / 2))
        result.append(int((min_sum_0 - min_sum_1 + min_sum_2) / 2))
        result.append(int((min_sum_1 + min_sum_2 - min_sum_0) / 2))
        result = deduce_numbers(sums, result, flag_used)
        if len(result) == n:
            break
        result.clear(); flag_used[2:] = [False] * (len(sums) - 2)
    # 如果结果长度为 n,返回结果;否则返回 "Impossible"
    return " ".join(map(str, result)) if len(result) == n else "Impossible"

if __name__ == "__main__":
    #  You can add more test cases here
    print(solution(3, [1269, 1160, 1663]) == "383 777 886")
    print(solution(3, [1, 1, 1]) == "Impossible")
    print(solution(4, [2, 2, 2, 2, 2, 2]) == "1 1 1 1")
    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")

3 时间和空间复杂度分析

3.1 时间复杂度分析

首先有排序,复杂度为mlogmmlogm,其中m = (n+1)n/2(n+1)*n/2, 再对递归调用部分分析,min_sum_3的种类最大可能为n2n-2,对于每一种递归的深度最大为n3n-3,在每一层中需要排除已知的最小x,这个最大的可能为(n+1)n/2(n+1)*n/2,所以总的时间复杂度为O(mlogm+(n2)(n3)(n+1)n/2)O(mlogm + (n-2)*(n-3)*(n+1)*n/2),简化为O(n4)O(n^4)。排除已知的最小值这部分可用二分查找,如果这样的话,时间复杂度可以进一步降低,为O(n3logn)O(n^3logn)。看上去依旧非常吓人,不过目前笔者还没有找到时间复杂度更低的算法,希望评论区的大佬可以贡献高招。

3.2 空间复杂度分析

首先递归调用共n层,每层保存两个长度为(n+1)n/2(n+1)*n/2的数组,空间复杂度就为O(n3)O(n^3)

4 总结

不得不说豆包AI助手真的太好用了。豆包是一款非常出色的AI助手,它不仅能够快速准确地回答各种编程问题,还能提供详细的解题思路和代码优化建议。豆包的智能程度和响应速度令人印象深刻,它极大地提高了我的编程效率和学习体验。无论是初学者还是有经验的开发者,都能从豆包的帮助中受益匪浅。感谢豆包的辛勤工作和卓越表现。这道题目就是豆包提供的思路,以后的编程体验会大大提升,同样的工作量能在更少的时间内完成了。掌握AI辅助编程绝对能提高自身的优势和竞争能力。