问题描述
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"
分析
设这个整数按非降序排列构成的列向量为, ; 设这个整数两两相加得到的个和按照某种次序排列构成的列向量为,。
易知,当时,无解。
当时,我们可以确定。 由此,是可以直接计算出来的。 若的元素是按非降序排序的,则有:
。 得
当时,与之间不存在上述确定性。以为例:
若 s.t.,则 中的个元素不一定是按照非降序排列的;更确切地说,我们不能确定 、是分别由中哪些元素相加得到的。 这是因为与(对应矩阵的第3、4行)之间的大小关系无法确定。
推而广之,给定,中的元素像上述那样“按序”相加。在这种情况下我们只能确定,而中其他元素的来源是不确定的, 中的元素不一定按照非降序排列。换言之, 若使中的元素按照非降序排列,则除了头两行和尾两行, 其余部分并不确定。
核心思路
-
因为满秩,所以要么无解,要么有唯一解。
- 我们只要算出了一个可行解,就可以将其作为最终答案。
- 若在穷尽了所有可能后,仍未获得一个可行解,则无解。
-
, , ;则
- 必有, , ;
-
穷尽所有的可能就是遍历所有可能的, 亦即除去已确定的, 从选出个元素;这可以通过递归实现;
- ;(参见题解第23行)
- 若为小数,则不符合要求,舍去
- 根据(其元素按照非降序排列)以及 当前对应的求解其余
- 验算:将结果两两相加,若求得的和在中没有出现(由于已排序,可使用二分查找),则不符合要求,舍去。
- ;(参见题解第23行)
时间复杂度
- 对进行排序:。
- 穷尽种可能:。
- ……
题解
#include <cstdlib>
#include <algorithm>
#include <string>
#include <vector>
struct info {
const int end, k, last_one, denom, end_k;
const std::vector<int>& sums;
};
struct result {
const int x1;
const std::vector<int> other_xs;
};
result combi(const info& cond, const std::vector<int>& indices, int begin, long long sum_with_x1s, long long sum_without_x1)
{
const int n_selected = indices.size();
if (n_selected == cond.k) {
for (int i = begin; i != cond.end; ++i)
sum_without_x1 += cond.sums[i];
const lldiv_t x1 = lldiv(cond.last_one * sum_with_x1s - sum_without_x1, cond.denom);
if (x1.rem)
return {};
std::vector<int> other_xs(cond.k);
for (int i = 0; i != cond.k; ++i)
other_xs[i] = cond.sums[indices[i]] - x1.quot;
for (int i = 0; i != cond.last_one; ++i)
for (int j = i + 1; j != cond.k; ++j)
if (!std::binary_search(cond.sums.begin() + 2, cond.sums.end(), other_xs[i] + other_xs[j]))
return {};
std::sort(other_xs.begin() + 2, other_xs.end());
return { (int)x1.quot, other_xs };
}
long long subtotal_without_x1 = 0;
int i = begin;
for (const int pale = cond.end_k + n_selected; i != pale; ++i) {
std::vector<int> new_indices(indices);
new_indices.push_back(i);
const result tmp = combi(cond, new_indices, i + 1, sum_with_x1s + cond.sums[i], sum_without_x1 + subtotal_without_x1);
if (tmp.other_xs.size())
return tmp;
subtotal_without_x1 += cond.sums[i];
}
std::vector<int> new_indices(indices);
new_indices.push_back(i);
return combi(cond, new_indices, i + 1, sum_with_x1s + cond.sums[i], sum_without_x1 + subtotal_without_x1);
}
std::string solution(const int n, std::vector<int>& sums)
{
const std::string FAIL = "Impossible";
if (n < 3)
return FAIL;
std::string ans;
std::sort(sums.begin(), sums.end());
if (n == 3) {
const ldiv_t x1 = ldiv(sums[0] + sums[1] - sums[2], 2);
if (x1.rem)
return FAIL;
ans += std::to_string(x1.quot);
for (int i = 0; i != 2; ++i)
ans += ' ' + std::to_string(sums[i] - x1.quot);
return ans;
}
const int n_sums = sums.size();
const int end = n_sums - 2;
const long btm2 = sums[0] + sums[1], top2 = sums[end] + sums.back();
const int k = n - 1;
const result tmp = combi({ end, k, k - 1, (k - 1) * k, end - k, sums }, { 0, 1 }, 2, btm2, top2);
if (tmp.other_xs.empty())
return FAIL;
ans += std::to_string(tmp.x1);
for (int i = 0; i != k; ++i)
ans += ' ' + std::to_string(tmp.other_xs[i]);
return ans;
}