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

181 阅读2分钟

和的逆运算问题

题目链接:和的逆运算问题 - MarsCode

问题描述

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"

题解思路

我们先对这n(n - 1) / 2 个和进行排序,不妨假设排序后的结果为:sum[1]sum[2]...sum[n(n1)2] sum[1] \le sum[2] \le ... \le sum[\frac{n(n-1)}{2}]

假设原来的n个数从小到大排序为:a[1]a[2]...a[n]a[1] \le a[2] \le ... \le a[n]。我们按照如下方式对其两两之和进行排列:

a[1]+a[2]a[1]+a[3]a[1]+a[4]...a[1]+a[n]  a[2]+a[3]a[2]+a[4]...a[2]+a[n]a[3]+a[4]...a[3]+a[n]   ...a[n1]+a[n]a[1]+a[2] \quad a[1]+a[3] \quad a[1]+a[4] \quad ... \quad a[1]+a[n] \\ \qquad \qquad \quad \ \ a[2]+a[3] \quad a[2]+a[4] \quad ... \quad a[2]+a[n] \\ \qquad \qquad \qquad \qquad \qquad \quad a[3]+a[4] \quad ... \quad a[3]+a[n] \\ \qquad \qquad \qquad \qquad \qquad \ \ \ \, ... \\ \qquad \qquad \qquad \qquad\qquad \qquad \qquad \qquad \qquad a[n-1]+a[n]

可以发现,每一行,从左到右,和依次递增,每一列,从上到下,和依次递增。于是可以推断出a[1]+a[2]=sum[1]a[1]+a[2]=sum[1],进一步可以推断出a[1]+a[3]=sum[2]a[1]+a[3]=sum[2]

但是,不一定有a[2]+a[3]=sum[3]a[2]+a[3]=sum[3]。这是因为a[2]+a[3]a[2]+a[3]a[1]+a[4], ... ,a[1]+a[n]a[1]+a[4], \ ... \ ,a[1]+a[n]之间的大小关系是不确定的。所以,我们需要对a[2]+a[3]a[2]+a[3]的取值进行讨论,它可能是在数组sum中第3 ~ n小的那个。

假设a[2]+a[3]=sum[k],3kna[2]+a[3]=sum[k],3 \le k \le n,根据a[1]+a[2]=sum[1],a[1]+a[3]=sum[2]a[1]+a[2]=sum[1],a[1]+a[3]=sum[2],我们可以求出a[1],a[2],a[3]a[1],a[2],a[3]的取值。

进而对于a[4]a[4],我们先将a[1]+a[2],a[1]+a[3],a[2]+a[3]a[1]+a[2], a[1]+a[3], a[2]+a[3]删除,于是可以发现a[1]+a[4]a[1]+a[4]变成最小值了。于是根据a[1]a[1],可以求出a[4]a[4]

同理,将a[1]+a[4],a[2]+a[4],a[3]+a[4]a[1]+a[4],a[2]+a[4],a[3]+a[4]删除。于是a[1]+a[5]a[1]+a[5]变成最小值了,就可以求出a[5]a[5]。以此类推,可以求出原来的每个数a[i],1ina[i],1 \le i \le n

注意:每个a[i]a[i]均为整数,且要满足a[1]a[2]...a[n]a[1] \le a[2] \le ... \le a[n],以及a[i]+a[j],0ij1a[i]+a[j],0 \le i \le j-1均在数组sumsum中。

渐进时间复杂度为:O(n3)O(n^3)

这里模拟下n=5n=5的情况,遍历a[2]+a[3]=sum[k],3k5a[2]+a[3]=sum[k],3 \le k \le 5所有可能的取值,进而求出a[1],a[2],a[3]a[1],a[2],a[3]。将a[1]+a[2],a[1]+a[3],a[2]+a[3]a[1]+a[2], a[1]+a[3], a[2]+a[3]删除。

a[1]+a[2]a[1]+a[3]a[1]+a[4]a[1]+a[5]  a[2]+a[3]a[2]+a[4]a[2]+a[5]a[3]+a[4]a[3]+a[5]  a[4]+a[5]\sout{a[1]+a[2]} \quad \sout{a[1]+a[3]} \quad a[1]+a[4] \quad a[1]+a[5] \\ \qquad \qquad \quad \ \ \sout{a[2]+a[3]} \quad a[2]+a[4] \quad a[2]+a[5] \\ \qquad \qquad \qquad \qquad \qquad \quad a[3]+a[4] \quad a[3]+a[5] \\ \qquad \qquad \qquad \qquad\qquad \qquad \qquad \quad \quad \; a[4]+a[5]

此时a[1]+a[4]a[1]+a[4]最小,进而求出a[4]a[4]。将a[1]+a[4],a[2]+a[4],a[3]+a[4]a[1]+a[4], a[2]+a[4], a[3]+a[4]删除。

a[1]+a[2]a[1]+a[3]a[1]+a[4]a[1]+a[5]  a[2]+a[3]a[2]+a[4]a[2]+a[5]a[3]+a[4]a[3]+a[5]  a[4]+a[5]\sout{a[1]+a[2]} \quad \sout{a[1]+a[3]} \quad \sout{a[1]+a[4]} \quad a[1]+a[5] \\ \qquad \qquad \quad \ \ \sout{a[2]+a[3]} \quad \sout{a[2]+a[4]} \quad a[2]+a[5] \\ \qquad \qquad \qquad \qquad \qquad \quad \sout{a[3]+a[4]} \quad a[3]+a[5] \\ \qquad \qquad \qquad \qquad\qquad \qquad \qquad \quad \quad \; a[4]+a[5]

此时a[1]+a[5]a[1]+a[5]最小,进而求出a[5]a[5]。将a[1]+a[5],a[2]+a[5],a[3]+a[5],a[4]+a[5]a[1]+a[5], a[2]+a[5], a[3]+a[5], a[4]+a[5]删除。此时,便可以求出a[1],a[2],a[3],a[4],a[5]a[1],a[2],a[3],a[4],a[5]

C++代码

#include <functional>
#include <iostream>
#include <vector>
#include <string>
#include<set>

std::string solution1(int n, std::vector<int>& nums, std::multiset<int> sums) {
    sums.erase(sums.find(nums[0] + nums[1]));
    sums.erase(sums.find(nums[0] + nums[2]));
    sums.erase(sums.find(nums[1] + nums[2]));
    for (int k = 3; k < n; k++) {
        nums[k] = *sums.begin() - nums[0];
        if (nums[k] < nums[k - 1]) return "Impossible";
        for (int l = 0; l < k; l++) {
            if (!sums.count(nums[l] + nums[k])) return "Impossible";
            sums.erase(sums.find(nums[l] + nums[k]));
        }
    }
    std::string result;
    for (int k = 0; k < n; k++) {
        result += std::to_string(nums[k]) + " ";
    }
    return result.substr(0, result.length() - 1);
}

std::string solution(int n, std::vector<int> sums) {
    // Please write your code here
    std::multiset<int> order_sums(sums.begin(), sums.end());
    std::vector<int> nums(n, 0);
    std::multiset<int>::iterator first = order_sums.begin();
    std::multiset<int>::iterator second = ++order_sums.begin();
    std::multiset<int>::iterator third = ++(++order_sums.begin());
    for (int k = 0; k < n - 2; k++) {
        if (!((*first + *second + *third) % 2)) {
            nums[0] = (*first + *second + *third) / 2 - *third;
            nums[1] = (*first + *second + *third) / 2 - *second;
            nums[2] = (*first + *second + *third) / 2 - *first;
            std::string result = solution1(n, nums, order_sums);
            if (result != "Impossible") return result;
        }
        third++;
    }
    return "Impossible";
}

int main() {
    // You can add more test cases here
    std::vector<int> sums = { 1269, 1160, 1663 };
    std::cout << (solution(3, sums) == "383 777 886") << std::endl;
    return 0;
}