和的逆运算问题
题目链接:和的逆运算问题 - 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[2n(n−1)]。
假设原来的n个数从小到大排序为:a[1]≤a[2]≤...≤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[n−1]+a[n]
可以发现,每一行,从左到右,和依次递增,每一列,从上到下,和依次递增。于是可以推断出a[1]+a[2]=sum[1],进一步可以推断出a[1]+a[3]=sum[2]。
但是,不一定有a[2]+a[3]=sum[3]。这是因为a[2]+a[3]与a[1]+a[4], ... ,a[1]+a[n]之间的大小关系是不确定的。所以,我们需要对a[2]+a[3]的取值进行讨论,它可能是在数组sum中第3 ~ n小的那个。
假设a[2]+a[3]=sum[k],3≤k≤n,根据a[1]+a[2]=sum[1],a[1]+a[3]=sum[2],我们可以求出a[1],a[2],a[3]的取值。
进而对于a[4],我们先将a[1]+a[2],a[1]+a[3],a[2]+a[3]删除,于是可以发现a[1]+a[4]变成最小值了。于是根据a[1],可以求出a[4]。
同理,将a[1]+a[4],a[2]+a[4],a[3]+a[4]删除。于是a[1]+a[5]变成最小值了,就可以求出a[5]。以此类推,可以求出原来的每个数a[i],1≤i≤n。
注意:每个a[i]均为整数,且要满足a[1]≤a[2]≤...≤a[n],以及a[i]+a[j],0≤i≤j−1均在数组sum中。
渐进时间复杂度为:O(n3)。
这里模拟下n=5的情况,遍历a[2]+a[3]=sum[k],3≤k≤5所有可能的取值,进而求出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[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]
此时a[1]+a[4]最小,进而求出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]
此时a[1]+a[5]最小,进而求出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]。
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) {
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() {
std::vector<int> sums = { 1269, 1160, 1663 };
std::cout << (solution(3, sums) == "383 777 886") << std::endl;
return 0;
}