Ich benötige Hilfe bei diesem ACM-ICPC-Problem. Meine aktuelle Idee ist es, dies als Problem mit dem kürzesten Weg zu modellieren, das unter der Problemstellung beschrieben wird.
Problem
Es gibt N = 1000
Kernabfallbehälter befindet sich entlang einer 1-D - Nummer Linie an unterschiedlichen Positionen aus -500,000 to 500,000
, mit der Ausnahme x=0
. Eine Person muss alle Mülleimer einsammeln. In jeder Sekunde, in der ein Abfallbehälter nicht eingesammelt wird, strahlt er 1 Einheit Strahlung aus. Die Person beginnt bei x = 0
und kann die 1
Einheit jede Sekunde bewegen , und das Sammeln des Abfalls dauert vernachlässigbar lange. Wir wollen die minimale Menge an Strahlung finden, die beim Sammeln aller Behälter freigesetzt wird.
Beispiel Input:
4
Container befinden sich bei [-12, -2, 3, 7]
.
Die beste Reihenfolge, um diese Container zu sammeln, ist [-2, 3, 7, -12]
eine minimale Emission von 50
Einheiten. Erklärung: Die Person geht -2
in 2 Sekunden zu und während dieser Zeit wird 2 units
Strahlung abgegeben. Dann geht er zu 3
(distance:), 5
so dass der Lauf 2 + 5 = 7
Strahlungseinheiten freigesetzt hat . Er braucht 4
mehr Sekunden, um dorthin zu gelangen, x = 7
wo das Fass 2 + 5 + 4 = 11
Einheiten abgegeben hat . Er braucht 19
Sekunden, um dorthin zu gelangen, x = -12
wo das Fass 2 + 5 + 4 + 19 = 30
Einheiten abgegeben hat . 2 + 7 + 11 + 30 = 50
, das ist die Antwort.
Anmerkungen
Es gibt eine offensichtliche O(N!)
Lösung. Ich habe mich jedoch mit gierigen Methoden befasst, wie zum Beispiel dem nächstgelegenen oder dem nächstgelegenen Cluster, aber diese haben nicht funktioniert.
Ich habe über dieses Problem eine ganze Weile nachgedacht und es als Grafiksuchproblem modelliert:
- Wir fügen
0
als Basisposition ein (Dies wird der Ausgangszustand sein) - Dann sortieren wir die Positionen vom kleinsten zum größten.
- Wir machen dann ein BFS / PFS, aus dem das
state
besteht- Zwei Ganzzahlen
l
undr
diese repräsentieren einen zusammenhängenden Bereich in dem sortierten Positionsarray, das wir bereits besucht haben - Eine Ganzzahl
loc
, die angibt, ob wir uns am linken oder am rechten Endpunkt des Bereichs befinden - Eine Ganzzahl
time
, die die verstrichene Zeit angibt - Eine Ganzzahl 'cost', die die Gesamtkosten angibt (basierend auf den von uns besuchten Knoten)
- Zwei Ganzzahlen
- Aus jedem Zustand können wir zu [l - 1, r] und [l, r + 1] wechseln und die anderen 3 Ganzzahlen entsprechend anpassen
- Der Endzustand ist [0, N] und überprüft beide Endpositionen.
Es scheint jedoch, dass [L, R, loc]
ein Zustand nicht eindeutig definiert wird, und wir müssen ihn speichern L, R, loc, and time
, während wir ihn cost
bei jedem einzelnen minimieren . Dies führt zu einem exponentiellen Algorithmus, der für alles Gute immer noch viel zu langsam ist.
Kann mir jemand helfen, meine Idee zu erweitern oder in die richtige Richtung zu lenken?
Bearbeiten: Vielleicht kann dies als Problem der Optimierung der dynamischen Programmierung modelliert werden? Wenn man darüber nachdenkt, hat es die gleichen Probleme wie die Graphensuchlösung - nur weil der Strom cost
niedrig ist, bedeutet dies nicht, dass es die optimale Antwort für dieses Unterproblem ist, da dies time
auch die Antwort stark beeinflusst.
Gierig funktioniert nicht: Ich habe einen Algorithmus für die gierige Auswahl, mit dem die Kosten für den Umzug an einen bestimmten Ort geschätzt werden (z. B. verdoppeln wir die Entfernung zu den linken Fässern und dergleichen, wenn wir uns nach rechts bewegen).
Könnten Sie eine Priority-First-Suche mit einer Heuristik durchführen? Die Heuristik kann die Kosten der aktuellen Reise mit der verstrichenen Zeit kombinieren.
quelle
Antworten:
Ich denke, Sie können dies verbessern, indem Sie das Problem als gerichteten Graphen von Positionspaaren betrachten.
In diesem Beispiel verwende ich die Zeile mit den Werten -9, -6, -1, 3 und 5.
Da es zu schwierig ist, eine Grafik nur mit Text zu zeichnen, werde ich die Paare als Tabelle darstellen. Wir können uns die Zellen als den Zustand vorstellen, in dem alle Container zwischen diesen beiden Positionen gesammelt wurden. Jede Zelle hat zwei Werte, von denen einer die Kosten für die Weiterleitung nach links und der andere die Kosten für die Weiterleitung nach rechts (zum nächsten Container) darstellt.
Die Werte können einfach als der Abstand zwischen den beiden Punkten multipliziert mit der Anzahl der Fässer außerhalb dieser beiden Punkte + 1 berechnet werden . Zellen, bei denen beide Nummern dasselbe Vorzeichen haben, stehen für Fälle, in denen alle Fässer mit dem entgegengesetzten Vorzeichen gesammelt wurden. Diese werden nur anhand der Anzahl der Fässer in Richtung von Null berechnet.
In der Tabelle bedeuten die Werte von X, dass Sie nicht in diese Richtung gehen können (alle Fässer in dieser Richtung wurden genommen). Die Zeilen repräsentieren die aktuelle Position des Kollektors und die Spalten repräsentieren die Position des nächsten gegenüberliegenden Zylinders.
Die Regeln für das Verschieben zwischen Feldern können etwas verwirrend sein.
Für negativ nummerierte Spalten gelten folgende Regeln:
Für positiv nummerierte Spalten gelten folgende Regeln:
Jetzt können wir den Algorithmus von Dijkstra ausführen, um den besten Pfad zu berechnen, wobei wir diese Bewegungsregeln verwenden, um den Graphen zu durchlaufen. Unsere Startpositionen sind (-1, 3) und (3, -1) mit anfänglichen Kosten von 5 bzw. 15. Sobald wir die Werte für die beiden Endpositionen (links von (-9, -9) und rechts von (5, 5)) berechnet haben, ist die kleinere der beiden unsere Antwort.
Die Laufzeit für jeden der Schritte beträgt:
Die ersten beiden Schritte werden vom letzten dominiert, daher ist Ihre Gesamtlaufzeit O (N ^ 2 log N), was für die Herausforderung eine ausreichend gute Laufzeit sein sollte.
quelle
Kürzeste Entfernung
Ich habe gestern eine Java-Anwendung geschrieben, um das Problem zu lösen. Das Problem ist im Grunde ein Problem der kürzesten Entfernung, wie SRJ in seinem Kommentar sagte. Die Strahlung zeigt nur, dass Sie einen Wert zusammen mit der kürzesten Entfernung akkumulieren können.
Grundsätzlich ist hier was ich getan habe.
Hier sind einige Ausgaben aus der Anwendung
Ich habe den Algorithmus in keiner Weise optimiert. Ich habe nicht einmal bemerkt, dass die Liste der eingegebenen Zahlen sortiert war. (Ich bin ein doofus.)
Als ich meinen Code mit den Maximalwerten, 1.000 Containern und einem Bereich von -500.000 bis 500.000 ausführte, dauerte die Ausführung meines Codes 3 Sekunden. Die meiste Zeit wurden die 1.000 Druckzeilen auf die Konsole geschrieben.
Ich bin keine große O-Person, aber ich denke, dass meine rohe Kraft, die den kürzesten Weg geht, O (N im Quadrat) ist, nicht O (N!).
Wenn ich die Tatsache ausnutzen würde, dass die eingegebenen Zahlen sortiert sind, so dass ich nur die beiden Zahlen auf beiden Seiten meines derzeitigen Standorts überprüfen müsste, könnte ich die Bewerbung in die Nähe von O (N) bringen. Ich muss nur 2 Zahlen überprüfen, da ich die Elemente von der Liste entferne, wenn ich zu ihnen komme.
Sie haben verschiedene Algorithmen wie den Greedy-Algorithmus verwendet, um eine ungefähre Lösung zu finden.
Wenn die Ausführung meines Programms 3 Stunden statt 3 Sekunden gedauert hätte, hätten Sie die Wahl.
Ist die Lösung gut genug gut genug?
Mit anderen Worten, bin ich bereit, die Verarbeitungsgeschwindigkeit gegen eine ausreichend gute Antwort einzutauschen?
Wenn eine ausreichend gute Antwort ausreichend ist, verwenden Sie Approximationsalgorithmen.
Wenn Sie die perfekte Antwort wollen, müssen Sie die kürzesten Wege gehen. Es gibt keine Abkürzung.
Wenn jemand möchte, dass ich meinen Code poste, werde ich das tun. Ich bin mir sicher, dass es immer noch Fehler gibt, da ich sehen wollte, ob ich einen Algorithmus für kürzestes Gehen implementieren kann.
quelle
Ich habe eine Lösung, die dieses Problem mit der
2^N
Zeit lösen kann , aber ich denke, sie ist eine hilfreiche Methode, um das Problem einzugrenzen. Deshalb dachte ich, ich würde posten.Anstatt das Problem als Diagramm zu modellieren, würde ich modellieren, dass es ein binärer Entscheidungsbaum ist (sagen wir
T
). In jedem Level musst du zwischen rechts und links wählen. Es ist ziemlich einfach, die "Kosten" jeder Kante zu berechnen. Seih(K)
die Höhe des aktuellen Knotens,K
dann die Kosten für die Kante, zu der es gehtleft_child(K) = h(K) x dist(K, left_child(K))
. Eine ähnliche Berechnung reicht für die Kosten der Kante zum richtigen Kind aus. Sie erstellen diesen Baum, verfolgen die kumulativen Kosten der Kanten bis zum Ende und geben den Pfad zum Blattknoten mit den geringsten Gesamtkosten an.Beachten Sie, dass die Kostenberechnung funktioniert, da die Länge jeder Kante
dist(K, left_child(K))
die Zeit darstellt, um zur nächsten Stelle zu gelangen, während die Höhe des Unterbaums die Anzahl der verbleibenden Stellen ist (z. B. noch Strahlung emittierend).Der Trick in diesem Framework besteht nun darin, festzustellen, ob es einige Heuristiken gibt, mit denen Sie "beweisen" können, dass Sie die Erweiterung der Suche entlang eines Zweigs ignorieren können. Meine Intuition ist, dass es für jede solche Heuristik eine Anordnung von Sites gibt, die sie besiegen, aber vielleicht kann sich jemand etwas einfallen lassen.
Einige haben vorgeschlagen, Lösungen mit kürzesten Wegen für Graphen anzuwenden. Ich habe einige Zweifel, ob eine solche Lösung funktionieren kann. Ihre Nachbarn in der Grafik des Problems ändern sich je nach dem Pfad, dem Sie folgen. Zum Beispiel in Ihrem ursprünglichen Beitrag mit,
[-12, -2, 3, 7]
wenn Sie zu-2
dann gehen-12
und3
"Nachbarn" werden, und wenn Sie zu3
dann gehen-2
und7
Nachbarn sind. Jedes mögliche "Paar" positiver und negativer Werte kann im endgültigen Diagramm möglicherweise Nachbarn sein. Ich kenne keine Algorithmen für kürzeste Wege, die in einem dynamischen Graphen nachweislich korrekt sind.quelle
Ich denke, es ist am sinnvollsten, sich jede Etappe einfach als eine binäre Wahl zwischen rechts zum nächsten Fass und links zum nächsten Fass vorzustellen. Haben Sie einfach eine Kostenfunktion, die die Anzahl der Strahlungseinheiten angibt, die insgesamt durch eine Bewegung entstehen würden, und wählen Sie diejenige mit den niedrigsten Kosten aus.
Betrachten Sie NICHT einfach das nächstgelegene Fass, sondern gehen Sie davon aus, dass Sie durch die Entfernung von einem Fass effektiv die doppelte Strahlung hinzufügen, da Sie durch die Entfernung auch die Kosten für das spätere Zurückbewegen in diese Richtung verursacht haben.
In Ihrem Beispiel von [-12, -2,3,7] würde eine Bewegung nach links insgesamt 14 (2 + 2 + 10) links und 18 (2 + 2 + 5 + 9) rechts verursachen. Wenn Sie sich nach rechts bewegen, werden 10 (3 + 3 + 4) rechts und 26 (3 + 3 + 5 + 15) rechts angezeigt. Klar links ist zunächst die richtige Lösung. Eine ähnliche Berechnung kann für jede aufeinanderfolgende Bewegung durchgeführt werden.
Danach reduziert sich das Problem im Wesentlichen auf eine Suche, daher sollte die Komplexität O (nlog (n)) sein, was VIEL besser ist als O (n!). Ich glaube, dass dies notwendigerweise die niedrigste Komplexität ist, die für dieses Problem existieren kann, da es sich im Grunde um einen vergleichsbasierten Suchalgorithmus handelt, für den es nicht möglich ist, eine bessere Leistung als O (nlog (n)) zu erzielen.
Anscheinend war mir diese Beschreibung nicht klar genug, und deshalb habe ich beschlossen, sie etwas programmatischer zu gestalten: 1. Berechnen Sie die Kosten für Links- und Rechtslauf anhand der aktuellen Position. 2. Ziehen Sie ein die günstigste Richtung 3. Entfernen Sie das erreichte Fass aus der Betrachtung bei der Berechnung der Kosten für das Bewegen in eine Richtung
Berechnung der Kosten: 1. Identifizieren Sie das nächstgelegene Fass in der angegebenen Richtung. Angenommen, $ dist ist die Entfernung von der aktuellen Position zum nächsten Fass in der angegebenen Richtung. 2. Die Kosten werden als N * $ dist initialisiert, wobei N nur noch aktive Fässer berücksichtigt. 3. Addieren Sie dazu den Abstand, den die durch $ dist angegebene neue Position von jedem verbleibenden Fass haben würde.
quelle
[43, -18, -98, -82, 63]
[-10,-11, 10,20,30,40,50,60,70]
. Die richtige und einzige Lösung besteht darin, alle negativen und dann die positiven zu sammeln. Für eine Antwort von 455.Teillösung - Ich werde später darauf zurückkommen.
Angenommen, die "Standard" -Strategie wird ganz nach links oder rechts ausgeführt, je nachdem, was billiger ist. Fragen Sie jetzt, ob es einen kleinen Abstecher wert ist, ein Fass in die andere Richtung zu holen. Es ist ziemlich einfach, die Antwort zu berechnen.
Für Sie ist es billiger, wenn Sie den gesamten Weg nach rechts laufen lassen, als wenn Sie den gesamten Weg nach links laufen lassen. Lohnt sich ein Abstecher nach -2? Es reduziert die Kosten für das Laufen ganz nach rechts und dann wieder auf 0 um 14 (weil Sie bei der Standardstrategie 4 Strahlungseinheiten pro Zug von 0 auf 3 "gezahlt" haben, jetzt sind es nur noch 3, Sie haben 3 von 3 gezahlt) bis 7, jetzt sind es 2, usw.), plus um eins pro Zug reduzieren sich Ihre Kosten für den Umzug von 0 auf -2, wodurch 2 weitere für insgesamt 16 gespart werden.
Es addiert jedoch die Kosten für den Wechsel von -2 zu 0 von 14 (4 Einheiten pro Zug zu -2, 3 Einheiten pro Zug zurück zu 0) für einen Nettogewinn von (16-14) = 2. Um dies zu berechnen, müssen Sie nicht die genauen Kosten für die Lösung des gesamten Problems für jede Entscheidung abschätzen. Sie verfügen über genügend Informationen, um zu entscheiden, ob das Laufen ganz nach links billiger ist als das Laufen ganz nach rechts und wie Viele Abfallbehälter befinden sich auf jeder Seite von Ihnen und der Abstand zur nächsten 2. Das ist also O (N ^ 2).
Abgesehen von einem wichtigen Problem - ich bin davon ausgegangen, dass Sie bis zum Ende durchstarten werden, und wir wissen, dass Sie sich möglicherweise verdoppeln werden. Um das zu bereinigen, müssen wir meine Berechnung aktualisieren. Bei der Probeneingabe habe ich angenommen, dass Sie 14 einsparen würden, indem Sie 1 weniger Gesamtstrahlung pro Einheit pro Sekunde emittieren, während Sie von 0 auf 7 und zurück laufen. Wenn Sie jedoch vor dem Laufen auf 7 zurückdrehen, werden die Einsparungen reduziert.
Das ist ziemlich schlimm, weil ich nicht weiß, wie ich das nächste Double-Back berechnen soll, ohne alle Möglichkeiten auszuprobieren, was uns zu O (2 ^ N) zurückführt.
Außer - es ist 2 ^ N mit dem Beschneiden. Ich habe berechnet, dass der "Abstecher" zu -2 14 gekostet hat, aber 16 gewonnen hat, wenn ich nicht mehr Abstecher hatte, bevor ich es bis zur ganz rechten Zahl geschafft habe. Wenn die am weitesten rechts stehende Zahl 5 gewesen wäre, würde ich sofort wissen, dass sich der Abstecher zu -2 nicht auszahlen könnte. (Kosten immer noch 14, maximaler Nutzen 12). Ich muss auch nicht in Betracht ziehen, zu -2 zu gehen und dann vor Erreichen von 6 einen Abstecher zu machen, da dies immer schlechter ist, als erst direkt zu diesem Punkt zu gelangen.
quelle
Ich denke, Sie können es mit einer Breitensuche lösen, indem Sie nicht mehr als 2 * N ^ 2 Tupel (boolean, int, int, int, string) beibehalten, wobei die Zeichenfolgen so lang sind, wie der Pfad kompliziert ist.
Die Tupel sind (min oder max boolesch, min zurückgelegte Position, max zurückgelegte Position, emittierte Gesamtstrahlung, Pfadverlauf).
Ich sehe den Algorithmus so:
Das Suchen und Entfernen dominierter Tupel verbessert die Leistung erheblich. Es kann sich lohnen, jedem Tupel ein Flag "Hat gezüchtet" hinzuzufügen und gezüchtete Tupel im Pool zu belassen.
Es müssen auch einige wichtige Entscheidungen getroffen werden, um zu entscheiden, wie die Tupel gespeichert werden sollen, und um sie nach Herrschaften und neuen Elementen zu durchsuchen, die gezüchtet werden sollen.
quelle