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

91 阅读4分钟

问题描述

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"

分析

设这nn个整数按非降序排列构成的列向量为x\bm{x}x{x1,x2,,xn}T\bm{x}\coloneqq\{x_1, x_2, \cdots, x_n\}^\mathrm{T}; 设这nn个整数两两相加得到的mn(n1)2\displaystyle m\coloneqq\frac{n(n-1)}{2}个和按照某种次序排列构成的列向量为y\bm{y}y{y1,y2,,ym}T\bm{y}\coloneqq\{y_1, y_2, \cdots, y_m\}^\mathrm{T}

易知,当  n2  \;n\leq2\;时,无解。

  n=3  \;n=3\;时,我们可以确定x1+x2<x1+x3<x2+x3x_1+x_2<x_1+x_3<x_2+x_3。 由此,x\bm{x}是可以直接计算出来的。 若y\bm{y}的元素是按非降序排序的,则有:

Ax=[110101011]x=y=[y1y2y3]\bm{A}\bm{x}=\begin{bmatrix} 1&1&0\\ 1&0&1\\ 0&1&1 \end{bmatrix}\bm{x}=\bm{y}=\begin{bmatrix} y_1\\ y_2\\ y_3 \end{bmatrix}。 得{x1=y1+y2y32x2=y1x1x3=y2x1\displaystyle\begin{cases} x_1=\dfrac{y_1+y_2-y_3}{2},\\ x_2=y_1-x_1,\\ x_3=y_2-x_1。 \end{cases}

  n4  \;n\geq4\;时,x\bm{x}y\bm{y}之间不存在上述确定性。以  n=4  \;n=4\;为例:

y={y1,y2,,y6}T  \exists\bm{y}=\{y_1, y_2, \cdots, y_6\}^\mathrm{T}\; s.t.  Ax=[11  1 1 1  1 11  1 1  11]x=y\;\bm{A}\bm{x}=\begin{bmatrix} 1&1&~&~\\ 1&~&1&~\\ 1&~&~&1\\ ~&1&1&~\\ ~&1&~&1\\ ~&~&1&1 \end{bmatrix}\bm{x}=\bm{y},则 y\bm{y} 中的66个元素不一定是按照非降序排列的;更确切地说,我们不能确定 y3y_3y4y_4是分别由  x  \;\bm{x}\;中哪些元素相加得到的。 这是因为x1+x4x_1+x_4x2+x3x_2+x_3(对应矩阵A\bm{A}的第3、4行)之间的大小关系无法确定。

推而广之,给定  n4  \;\forall n\geq4\;x  \bm{x}\;中的元素像上述  A  \;\bm{A}\;那样“按序”相加。在这种情况下我们只能确定{y1=x1+x2y2=x1+x3ym1=xn+xn2ym=xn+xn1\begin{cases} y_1&=x_1+x_2\\ y_2&=x_1+x_3\\ y_{m-1}&=x_n+x_{n-2}\\ y_m&=x_n+x_{n-1} \end{cases},而y\bm{y}中其他元素的来源是不确定的, y  \bm{y}\;中的元素不一定按照非降序排列。换言之, 若使  y  \;\bm{y}\;中的元素按照非降序排列,则  A  \;\bm{A}\;除了头两行和尾两行, 其余部分并不确定。

核心思路

  • 因为A\bm{A}满秩,所以Ax=y\bm{A}\bm{x}=\bm{y}要么无解,要么有唯一解。

    • 我们只要算出了一个可行解,就可以将其作为最终答案。
    • 若在穷尽了所有可能后,仍未获得一个可行解,则无解。
  • U{1,2,,m}\mathcal{U}\coloneqq\{1, 2, \cdots, m\}S1{iAi,1=1}\mathcal{S}_1\coloneqq\{i\mid \bm{A}_{i, 1}=1\}S0US1\mathcal{S}_0\coloneqq\complement_\mathcal{U}\mathcal{S}_1;则

    • 必有S1=n1\lvert\mathcal{S}_1\rvert=n-1{1,2}S1\{1, 2\}\subsetneqq\mathcal{S}_1{m,m1}S0\{m, m-1\}\subsetneqq\mathcal{S}_0
  • 穷尽所有的可能就是遍历所有可能的S1\mathcal{S}_1, 亦即除去已确定的{1,2}\{1, 2\}, 从U{1,2,m1,m}\mathcal{U}\setminus\{1, 2, m-1, m\}选出n3n-3个元素;这可以通过递归实现;

    • x1=[(n2)×iS1yi]iS0yi(n1)(n2)\displaystyle x_1=\frac{\left[(n-2)\times \sum\limits_{i\in\mathcal{S}_1}y_i\right]- \sum\limits_{i\in\mathcal{S}_0}y_i} {(n-1)(n-2)};(参见题解第23行)
      • x1x_1若为小数,则不符合要求,舍去
    • 根据  y\;\bm{y}(其元素按照非降序排列)以及 当前  S1  \;\mathcal{S}_1\;对应的A\bm{A}求解其余  xi  \;x_i\;
    • 验算:将结果两两相加,若求得的和在  y\;\bm{y}中没有出现(由于  y\;\bm{y}已排序,可使用二分查找),则不符合要求,舍去。

时间复杂度

  •   y  \;\bm{y}\;进行排序:O(mlogm)=O(n2logn)\displaystyle O(m\log m)=O(n^2\log n)
  • 穷尽(m4n3)\displaystyle{m-4\choose n-3}种可能:O(n!)\color{red}{O(n!)}
  • ……

题解

#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;
}