赛场上至少思考了俩小时,然后遇上一堆问题就未果了。

给定序列 \(a\) 问能否构造出 \(b\) 使得 \(a\) 中每个数都由 \(b\) 中数的差得到。

赛场上一直在研究奇怪的性质,比如三个数要怎么样才可以,四个数要怎么样才可以。遗憾的是,这些都是充分条件,但不必要。

其实一开始想的类似差分约束的图才是正确的思路。
把作差理解成一条有向边,那么就是要得到一张 \(n\) 个点 \(n\) 条边的图。若不考虑方向,这个图必然会有至少一个换,如果一个环都没有,那肯定不行。

所以问题变成了能否在图中找出这样的一个环,如果有,那一定可以,如果没有就一定不可以。
因为相减的顺序可以变化,所以这个环中数的正负性以及相减顺序其实是没有影响的,那么我们可以规定一个方向,然后再枚举所有可能的正负性。
设这环中点分别是 \(v_1, v_2, \cdots v_k\),那么肯定有 \((v_1 - v_2) + (v_2 - v_3) + \cdots + (v_k - v_1) = 0\),所以只要看 \(a\) 中有没有一些正负随意可以组成 \(0\) 就好了。

这样可以用背包完成,或者直接枚举是不是在环上并枚举正负。
时间复杂度是 \(O(n^2 \max a)\)\(O(3^n)\) 的。

代码
#include <iostream>
#include <vector>
#include <algorithm>
int a[15], p[15], T, n;
std::vector<int> t;
int abs(int x) { return (x < 0) ? -x : x; }
int main() {
    std::cin >> T;
    while (T--) {
        std::cin >> n;
        int sta = 1;
        bool flag = 0;
        for (int i = 1; i <= n; i++) std::cin >> a[i], sta *= 3;
        for (int i = 1; i < sta; i++) {
            int S = i, sum = 0;
            for (int i = 1; i <= n; i++) {
                int t = S % 3;
                S /= 3;
                if (t == 1) sum += a[i];
                else if (t == 2) sum -= a[i];
            }
            if (sum == 0) { flag = 1; std::cout << "YES\n"; break; }
        }
        if (!flag) std::cout << "NO\n";
    }
}

这个想法挺妙的,但似乎也没有什么具体的算法在,在训练上也只能通过不断做这样的思维题来达到了。