题目分析
题目要求我们根据 n 个整数两两相加得到的 n(n - 1) / 2 个和,找出原来的 n 个整数。这是一个典型的数学问题,可以通过构建方程组来解决。
关键点在于:
- 每个整数与其他整数相加会得到 n-1 个和。
- 所有和的总和是 n 个整数的总和的 n-1 倍。
- 通过这些和,我们需要反推出原来的 n 个整数。
解题思路
- 计算总和:首先计算所有和的总和,如果这个总和不能被 n-1 整除,则说明没有解,因为这意味着原始整数的总和无法满足条件。
- 确定第一个整数:由于每个整数都与其他整数相加,我们可以通过总和除以 n-1 来确定所有整数的总和,再减去任意一个和,得到第一个整数的值。
- 构建方程组:使用一个映射(map)来记录每个和出现的次数。通过第一个整数的值,我们可以计算出其他整数与第一个整数相加的和,并逐步确定其他整数的值。
- 验证和填充:对于每个可能的第一个整数的值,我们尝试构建完整的整数列表。如果成功,则返回这些整数;如果失败,则尝试下一个可能的值。
代码解析
#include <bits/stdc++.h>
using namespace std;
string solution(int n, vector<int> sums) {
int sum = 0;
for (auto s : sums)
sum += s; // 计算所有和的总和
if (sum % (n - 1) != 0)
return "Impossible"; // 如果总和不能被 n-1 整除,则返回 "Impossible"
vector<int> ans(n);
sort(sums.begin(), sums.end()); // 对和进行排序,方便后续处理
for (int i = -abs(sums[0]); i <= abs(sums[0]); i++) { // 尝试所有可能的第一个整数值
ans[0] = i;
map<int, int> p;
for (auto s : sums)
p[s]++; // 记录每个和出现的次数
int idx = 1;
for (auto [k, v] : p) { // 遍历每个和
if (!v)
continue;
while (p[k]) { // 当和还有剩余时
ans[idx] = k - ans[0]; // 计算当前整数的值
int t;
for (t = 0; t < idx; t++) { // 验证当前整数是否满足其他和
if (p[ans[idx] + ans[t]])
p[ans[idx] + ans[t]]--;
else
break;
}
if (t == idx)
idx++; // 如果验证成功,则移动到下一个整数
else
break; // 如果验证失败,则尝试下一个可能的第一个整数值
}
}
if (idx == n)
break; // 如果找到了所有整数,则退出循环
}
string res = "";
for (int i = 0; i < n - 1; i++)
res = res + to_string(ans[i]) + " "; // 构建结果字符串
return res + to_string(ans[n - 1]); // 返回最终结果
}
int main() {
// 测试用例
vector<int> sums1 = {1269, 1160, 1663};
vector<int> sums2 = {1, 1, 1};
vector<int> sums3 = {226, 223, 225, 224, 227, 229, 228, 226, 225, 227};
vector<int> sums4 = {-1, 0, -1, -2, 1, 0, -1, 1, 0, -1};
vector<int> sums5 = {79950, 79936, 79942, 79962, 79954,
79972, 79960, 79968, 79924, 79932};
// 输出测试结果
cout << (solution(3, sums1) == "383 777 886") << endl;
cout << (solution(3, sums2) == "Impossible") << endl;
cout << (solution(5, sums3) == "111 112 113 114 115") << endl;
cout << (solution(5, sums4) == "-1 -1 0 0 1")