Autor Beitrag
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Mi 26.12.12 01:56 
Nun ist auch die letzte Paranuss ausgewertet, also schreiten wir zur Musterlösung:

Um dieses Problem effizient zu lösen, ist die entscheidende Idee, sich erst einmal ein anderes Problem zu stellen: Angenommen, wir haben eine Zielfüllung x, die wir in allen Schneekanonen erreichen wollen, bereits gegeben - wie können wir entscheiden, ob diese erreichbar ist? Den theoretischer Angehauchten unter euch dürfte diese Umformulierung bekannt vorkommen: Das ist das zu unserem Optimierungsproblem gehörige Entscheidungsproblem. In der Komplexitätstheorie sieht man diese beiden als äquivalent an, denn sie lassen sich polynomiell ineinander überführen - haben wir einen Algorithmus für das zweite Problem gefunden, finden wir per binärer Suche schnell die maximale Zielfüllung :idea: !

Wir sind also fein raus, wenn wir das zweite Problem effizient lösen können. Und das sieht nicht nur einfacher aus, es lässt sich tatsächlich greedy lösen:
Zuerst einmal eine selbsterklärende, aber wichtige Beobachtung: Wir müssen nur Transporte zwischen direkten Nachbarn berücksichtigen, denn jeder längere lässt sich als Folge solcher realisieren. Fangen wir bei der ersten Schneekanone an und vergleichen deren Füllung y mit der Zielfüllung x. Ist sie größer, sind wir beruhigt und können die Differenz direkt zum rechten Nachbarn tragen, ansonsten müssen wir die fehlende Menge von diesem klauen (nicht dadurch verwirren lassen, dass dessen Füllmenge dadurch temporär negativ werden könnte). In jedem Fall ist die Zielfüllung nun erreicht und wir können einen Schritt nach rechts gehen. Bei der nächsten Kanone finden wir nun aber die gleiche Situation vor - wir müssen die Differenz wieder nach rechts schieben oder von dort einfordern, denn alle Kanonen links von uns sind haben sowohl die Zielfüllmenge erreicht als auch nichts mehr nach rechts abzugeben. Also können wir die Strategie weiterfahren und sehen, dass die Probleminstanz genau dann lösbar ist, wenn wir in der letzten Kanone mindestens die Zielfüllmenge vorfinden.
Und genauso einfach sieht das auch im Code aus (oder einfacher, wenn ich mir den Textblock oben nochmal anschaue):

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
bool HasSolution(long x){
  long delta = canons[0].Value - x; // Füllmenge zusätzlich zur Zielfüllmenge (kann auch negativ sein)

  for(int i = 1; i < n; i++){
    long carry = delta - (canons[i].Pos - canons[i-1].Pos); // Trage delta zur nächsten Kanone
    if(delta >= 0 && carry < 0)
      carry = 0// Ups, der Eimer ist auf dem Weg leergelaufen

    delta = (canons[i].Value - x) + carry;
  }

  return delta >= 0;
}

Zusammen mit der binären Suche kommt eine Komplexität von θ(n * max(y)) heraus - womit jede Eingabe in den gegebenen Schranken quasi instantan lösbar ist.

_________________
>λ=

Für diesen Beitrag haben gedankt: BenBE, Christian S., Mathematiker, Yogu
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Mi 26.12.12 10:18 
Glückwunsch an alle, die das gelöst haben.

Ich werde dann das nächste mal 10^5 im Taschenrechner ausrechnen oder auf Eingabevalidierung verzichten :autsch: :bawling:
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1652
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Mi 26.12.12 13:17 
Hallo,

ich habe mir die Zähne daran ausgebissen, wie ich es in einem Rutsch hinbekomme, also ohne binäre Suche.
Ich habe dazu einen Vektor erstellt, der zu Beginn== 0 die theoretische Menge von Kanone[x] in Bezug auf Kanone 0 enthält.
Oder anders gesagt, welchen Beitrag kann Kanone[x] an Kanone[0] liefern.
Dann suche ich rückwärts von der entferntesten Kanone runter zur Ausgangskanone die erste mit positivem Beitrag.
Von 0..X berechne ich die Durchschnittsmenge und packe dann Elemente rechts (x+?) davon hinzu, solange der Durchschnitt steigt.
So mache ab dort dann ebenso weiter.
Somit komme ich in einem Durchgang 0..n das Ergebnis.

Ich hänge es mal an,

Gruß Horst
P.S.
EDIT: Natürlich ist das nicht schneller.
Wenn man 10000 mal in jeder Schneekanone die gleiche Wassermenge hat, sind das n*(n-1)/2 Additionen.
Während binäre Suche ja konstant nur n*lb(MaxMenge) Durchgänge hat. Bei Int64 eben 64*n.
Einloggen, um Attachments anzusehen!


Zuletzt bearbeitet von Horst_H am Do 27.12.12 13:12, insgesamt 1-mal bearbeitet
Flamefire
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: Mi 26.12.12 15:48 
nachdem ich das mit den benachbarten Kanonen erkannt hatte war es recht einfach. mittels binarer Suche ließ sich dass tatsächlich in linearer Zeit lösen. hab allerdings unnötig den fullstand im Array gespeichert und musste dann immer Speicher kopieren...
aber immerhin gelöst
Mathematiker
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Do 27.12.12 15:48 
Hallo,
meine Lösung war klar und deutlich gesagt: falsch!
Nachdem ich user profile iconKhas Erklärung gelesen habe, hat es mir keine Ruhe gelassen. Ich musste versuchen, dies auch mit Delphi hinzubekommen, zumal ich kein C# habe und es auch nur in Ansätzen kann; eigentlich gar nicht.

Für alle, die eine Delphi-Lösung suchen, hänge ich die Lösung an; allerdings nicht als Konsolen-Programm; das mag ich nämlich nicht. Das Programm ermittelt auch richtig für
5
5 70
15 100
300 20
340 120
500 120
die korrekte Lösung 50. Außerdem bekomme ich jetzt auch für eine freundlicher Weise von user profile iconMartok zur Verfügung gestellte Datei mit 100000 Schneekanonen den richtigen Wert.

Im Nachhinein muss ich feststellen, dass die Idee das Problem "herumzudrehen", genial ist. Eigentlich hätte man darauf kommen müssen, eigentlich ...

Beste Grüße
Mathematiker
Einloggen, um Attachments anzusehen!
_________________
Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein