Optymalna wartość bezwzględna sumy - szybki algorytm

0

Jest jakiś szybki algorytm, żeby znaleźć minimalną wartość takiej sumy:
| \sum_{i=1}^{n} \pm|a_i| |
dla odpowiednio za dużego n, żeby można było sprawdzić każdą z 2^{n} możliwości.

Z góry dzięki!

0

Tak, wszystko z minusem - mniej nie będzie.

0

No tak, racja... :p zapomniałem modułu dopisać. Ta suma musi być najbliżej zera...

0
  1. Sortujesz nie rosnąco.
  2. Ustalasz sumę na zero.
  3. Dla każdej liczby: jeżeli suma większa od zera to odejmujesz liczbę, w przeciwnym wypadku dodajesz.

Przeważnie tan algorytm da dosyć dobry wynik, ale nie ma gwarancji że najlepszy,

0
 
int min = 0;
Collections.sort(predkosci, Collections.reverseOrder());
for(Integer predkosc : predkosci) {
	min += (Math.abs(min - predkosc) < Math.abs(min + predkosc)) ? -predkosc : predkosc;
	min = Math.abs(min);
}

Dzięki... coś tu jest nie tak?

0
for(Integer predkosc : predkosci) min +=(min>0)?-predkosc:predkosc;

Pamiętaj że nie ma gwarancji że dostaniesz optymalną wartość.
Jakiego rzędu jest suma tych prędkości oraz ich liczebność?
Bo można dynamiczne programowanie zaadaptować.

0

Podoba mi się Twoje rozwiązanie, bo jest jeszcze prostsze. Na koniec trzeba wziąć tylko moduł z tego.

Danych nie jest dużo, ale mimo to coś robię źle.
Zadanie

0

Może ci się ten algorytm podoba tylko że zdecydowanie nie podoba się SPOJ'owi i słusznie.

0

A mniej więcej gdzie tkwi błąd w rozumowaniu?

0

8 7 5 5 5 - minimalny wynik 0, algorytm który podałem zwróci 4

0

Rzeczywiście... ale problem dobrze opisałem tym wzorem... Zobaczymy czy coś wymyślę :p

0

Do tego dynamiczne programowanie warto spróbować.

0

Moja propozycja :]

import scala.Array.ofDim
import scala.io.StdIn

object Main {
  def solve(a: Array[Int], start: Int, count: Int): Set[Int] = {
    if (count > 1) {
      val p1 = solve(a, start, count / 2)
      val p2 = solve(a, start + count / 2, count - count / 2)
      p1.flatMap(x1 => p2.flatMap(x2 => Set(x1 + x2, Math.abs(x1 - x2))))
    } else {
      assert(count == 1)
      Set(a(start))
    }
  }

  def main(args: Array[String]): Unit = {
    for (z <- 0 until StdIn.readInt()) {
      val n = StdIn.readInt()
      val a = ofDim[Int](n)
      for (i <- 0 until n) {
        a(i) = StdIn.readInt()
      }
      val min = solve(a, 0, a.length).min
      val max = a.sum
      println(s"$min $max")
    }
  }
}

Plus wersja niby trochę zoptymalizowana, ale dalej przekraczająca limit czasu:

import scala.Array.ofDim

object Main {
  val MaxSum = 10000
  val Threshold = 100

  def solveSmall(a: Array[Int], start: Int, count: Int): Set[Int] = {
    if (count > 1) {
      val p1 = solveSmall(a, start, count / 2)
      val p2 = solveSmall(a, start + count / 2, count - count / 2)
      p1.flatMap(x1 => p2.flatMap(x2 => Set(x1 + x2, Math.abs(x1 - x2))))
    } else {
      assert(count == 1)
      Set(a(start))
    }
  }

  def convert(s: Set[Int]): Array[Boolean] = {
    val a = ofDim[Boolean](s.max + 1)
    s.foreach(a(_) = true)
    a
  }

  def solveBig(a: Array[Int], start: Int, count: Int): Array[Boolean] = {
    val (p1, p2) = if (count > Threshold) {
      (solveBig(a, start, count / 2),
        solveBig(a, start + count / 2, count - count / 2))
    } else {
      (convert(solveSmall(a, start, count / 2)),
        convert(solveSmall(a, start + count / 2, count - count / 2)))
    }
    val r = ofDim[Boolean](Math.min(MaxSum + 1, p1.length + p2.length - 1))
    for (i <- p1.indices if p1(i); j <- p2.indices if p2(j)) {
      r(i + j) = true
      r(Math.abs(i - j)) = true
    }
    r
  }

  def solve(a: Array[Int]): Int = {
    if (a.length > Threshold) {
      solveBig(a, 0, a.length).indexWhere(identity)
    } else {
      solveSmall(a, 0, a.length).min
    }
  }

  def main(args: Array[String]): Unit = {
    for (z <- 0 until readInt()) {
      val n = readInt()
      val a = ofDim[Int](n)
      (0 until n).foreach(a(_) = readInt())
      val min = solve(a)
      val max = a.sum
      println(min + " " + max)
    }
  }
}
0

Niestety słabo rozumiem Twój pomysł.

Ale właśnie mi teraz zaakceptowało moje rozwiązanie.

Jego Idea jest taka...
Tworzę tablicę o wartościach boolean o rozmiarze równym maksymalnej sumie wszystkich prędkości dla danego przypadku + 1 (plus jeden żeby indeks mógł odpowiadać pewnej sumie).
Watość domyślną wartości tabelki ustawiam na false.
Dla pierwszej wczytywanej liczby ustawiam possible[pierwsza wczytana] na true.

Dla każdej kolejnej wczytanej liczby j, jeśli dla pewnego k possible[k] to true, ustawiam possible[Math.abs(k-j)] oraz possible[k+j] na true, a samo possible[k] na false, korzystając z tabelki pomocniczej.
Znajduję najmniejszy indeks dla którego possible[index] to true.

import java.io.*;
import java.util.*;

public class MainSPEED2 {
	public static void main(String[] args) throws Exception {
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		for(int t = Integer.parseInt(in.readLine().trim()); t > 0; t--) {
			int min = 0;
			int max = 0;
			int size = Integer.parseInt(in.readLine());
			int[] numbers = new int[size];
			
			for(int i = 0; i < size; i++) {
				numbers[i] = Integer.parseInt(in.readLine().trim());
				max += numbers[i];
			}
			
			boolean[] possible = new boolean[max+1];
			Arrays.fill(possible, false);
			possible[numbers[0]] = true;
			boolean[] temp = new boolean[max+1];
			
			for(int j = 1; j < size; j++) {
				Arrays.fill(temp, false);
				for(int k = 0; k <= max; k++) {
					if(possible[k]) {
						temp[k + numbers[j]] = true;
						temp[Math.abs(k - numbers[j])] = true;
					}
				}
				possible = temp.clone();
			}
			
			for(int i = 0; i <= max; i++) {
				if (possible[i]) {
					min = i;
					break;
				}
			}
			
			System.out.println(min + " " + max);
		}
	}
}
0

Skoro przeszło to spoko. Ja chyba trochę przekombinowałem :P A że nie mam dostępu do danych testowych to nie sprofiluję kodu na nich.

1 użytkowników online, w tym zalogowanych: 0, gości: 1