替换等式的运算符,使得总和等于零

时间:2016-08-03 10:58:26

标签: algorithm recursion dynamic-programming backtracking

我给出了像这样的等式:

lapply(mget(paste0("DF", 1:7)), transform, 
                Interval = ((Upperbound - Lowerbound)/actual)/2)

如何以最佳方式替换运算符,使方程之和等于零,或打印n = 7 1 + 1 - 4 - 4 - 4 - 2 - 2 。我想到了一种算法,但它不是最优的。我有一个想法强制复杂-1,但O(n*2^n)

的所有案例

以下是问题的链接:http://codeforces.com/gym/100989/problem/M

3 个答案:

答案 0 :(得分:5)

您可以使用动态编程解决此问题。保留所有可能的部分总和的映射(映射到达到此总和的最小更改数),然后一次更新一个数字,

这是一个简洁的Python解决方案:

def signs(nums):
    xs = {nums[0]: 0}
    for num in nums[1:]:
        ys = dict()
        for d, k in xs.iteritems():
            for cost, n in enumerate([num, -num]):
                ys[d+n] = min(ys.get(d+n, 1e100), k+cost)
        xs = ys
    return xs.get(0, -1)

print signs([1, 1, -4, -4, -4, -2, -2])

理论上,这在最坏的情况下具有指数复杂性(因为部分和的数量在每一步都可以加倍)。但是,如果(如此处)给定的数字总是(有界)小的整数,那么部分和的数量会线性增长,并且程序在O(n ^ 2)时间内工作。

稍微优化的版本使用排序数组(小计,成本)而不是字典。可以丢弃太大或太小的部分和(假设所有剩余元素都在-300和+300之间,则不可能最终为0)。它的运行速度大约是其两倍,并且是一种更自然的实现,可以移植到比Python更低级别的语言,以获得最大速度。

def merge(xs, num):
    i = j = 0
    ci = 0 if num >= 0 else 1
    cj = 0 if num < 0 else 1
    num = abs(num)
    while j < len(xs):
        if xs[i][0] + num < xs[j][0] - num:
            yield (xs[i][0] + num, xs[i][1] + ci)
            i += 1
        elif xs[i][0] + num > xs[j][0] - num:
            yield  (xs[j][0] - num, xs[j][1] + cj)
            j += 1
        else:
            yield (xs[i][0] + num, min(xs[i][1] + ci, xs[j][1] + cj))
            i += 1
            j += 1
    while i < len(xs):
        yield (xs[i][0] + num, xs[i][1] + ci)
        i += 1

def signs2(nums):
    xs = [(nums[0], 0)]
    for i in xrange(1, len(nums)):
        limit = (len(nums) - 1 - i) * 300
        xs = [x for x in merge(xs, nums[i]) if -limit <= x[0] <= limit]
    for x, c in xs:
        if x == 0: return c
    return -1

print signs2([1, 1, -4, -4, -4, -2, -2])

答案 1 :(得分:1)

以下是C ++中的实现:

unordered_map <int, int> M, U;
unordered_map<int, int>::iterator it;
int a[] = {1, -1, 4, -4};

int solve() {
    for(int i = 0; i < n; ++i) {
        if(i == 0) M[a[i]] = 1;
        else {
            vector <pair <int, int>> vi;
            for(it = M.begin(); it != M.end(); ++it) {
                int k = it->first, d = it->second;
                vi.push_back({k + a[i], d});
                vi.push_back({k - a[i], d + 1});
            }
            for(int j = 0; j < vi.size(); ++j) M[vi[j].first] = MAXN;
            for(int j = 0; j < vi.size(); ++j) {
                M[vi[j].first] = min(M[vi[j].first], vi[j].second);
            }
        }
    }
    return (M[0] == 0 ? -1 : M[0] - 1);
}

答案 2 :(得分:0)

我能想到的是:

您计算原始等式。这导致-14。 现在你对数字进行排序(考虑到他们的+或 - ) 当等式导致负数时,您会查找最大数字来确定等式。如果数字太大,则跳过它。

orig_eq = -14

排序后:

-4,-4,-4,-2,-2,1,1

如果方程式orig_eq - 当前数字接近于零,则循环显示并选择每个数字。

这样您就可以选择每个数字来改变

的符号