2016-08-03 4 views
6

に等しくなるように、式の演算子を置き換えます合計が、私はこのような方程式を与えてるゼロ

n = 7 
1 + 1 - 4 - 4 - 4 - 2 - 2 

式の合計が等しくなるように、どのように最適に、演算子を置き換えることができますゼロ、または印刷-1。私は1つのアルゴリズムを考えますが、最適ではありません。私は、複雑さがすべてO(n*2^n)だが、(n < 300)のすべてのケースをブルートフォースする考えがあります。

ここに問題のリンク:http://codeforces.com/gym/100989/problem/Mがあります。あなたは元の方程式を計算

:何を私は考えることができる

+0

一つの方法は、 '+ 4ということを理解することであろう - 4 + - 4'は'と同じです4 '。 –

+0

奇数の印刷-1の場合、総パリティを取得します。 – Vesper

+0

いいえ、数字は '1 <= x <300'です。 – user2660964

答えて

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); 
} 
0

。これは-14になります。 数値をソートする(+または - を考慮に入れて) 方程式の結果が負の数になると、最大の数を探して方程式を修正します。数字が大きすぎる場合は、スキップします。

ソート後

orig_eq = -14

-4、-4、-4、-2、-2、1、1

次ループこの上及び各番号を選択した場合方程式orig_eq - 現在の数はゼロに近い。

この方法であなたは、動的プログラミングでこれを解決することができ

+0

複雑さは何ですか? – user2660964

+0

このアルゴリズムは "5-4-3 + 3-3"でどのように機能しますか?合計は-2ですが、単一の数値を変更して0に近づけることはできません。しかし、5 + 4-3-3-3は0です。 –

+0

パーティションの問題はNP完全であるため、保証された多項式時間解。 –

5

の符号を変更するには、各番号を選択することができます。で

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]) 

:ここでは簡潔なPythonのソリューションがありますすべての可能な部分和(変更の最小数へのマッピングこの合計に到達する)のマップを維持し、その後、一度それを1つの番号を更新し、

この理論では、最悪の場合に指数関数的な複雑さがあります(各ステップで部分和の数が2倍になる可能性があるため)。しかし、(ここでのように)与えられた数が常に小(int)であれば、部分和の数は線形に増加し、プログラムはO(n^2)時間で動作します。

さらに最適化されたバージョンでは、dictの代わりにソートされた配列(小計、コスト)が使用されます。大きすぎるか小さすぎる部分和を捨てることができます(残りの要素のすべてが-300と+300の間であると仮定すると、0に終わることは不可能になります)。これは約2倍の速さで実行され、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]) 
+0

スワップするたびにコストが1増加するはずですが、ここでは「コスト」で1を読んでいません。 – Vesper

+0

@ Vesper私はコードがちょっと難しいと認めますが、numはcost = 0、-numはcost = 1です。 –

+0

印象的です。そして、300未満の数では、 'ys 'のサイズは決して180000を超えないので、実行可能です。全体の複雑さは最悪の場合180000 * nなので、これは "数字の合計が奇数の場合-1を返す"と "d + nがモジュールによって合計/ 2を超える場合"のような事前チェックで最適化する必要があると思います'ys'に追加しないでください。 – Vesper

関連する問題