Visiter des points sur une droite numérique tout en minimisant un coût non lié à la distance


18

J'ai besoin d'aide sur ce problème ACM ICPC. Mon idée actuelle est de modéliser cela comme un problème de chemin le plus court, qui est décrit sous l'énoncé du problème.

Problème

Il y a N = 1000des conteneurs de déchets nucléaires situés le long d'une ligne numérique 1-D à des positions distinctes de -500,000 to 500,000, sauf x=0. Une personne est chargée de collecter toutes les poubelles. Chaque seconde où un conteneur à déchets n'est pas collecté, il émet 1 unité de rayonnement. La personne commence x = 0et peut déplacer l' 1unité à chaque seconde, et la collecte des déchets prend un temps négligeable. Nous voulons trouver la quantité minimale de rayonnement libérée lors de la collecte de tous les conteneurs.

Exemple d'entrée:

4Conteneurs situés à [-12, -2, 3, 7].

Le meilleur ordre pour collecter ces conteneurs est [-2, 3, 7, -12], pour un minimum d'émission d' 50unités. Explication: la personne passe -2en 2 secondes et pendant ce temps 2 unitsdes radiations sont émises. Il se rend ensuite à 3(distance:) 5pour que le baril libère des 2 + 5 = 7unités de rayonnement. Il prend 4plus de secondes pour arriver x = 7là où ce baril a émis des 2 + 5 + 4 = 11unités. Il prend 19quelques secondes pour arriver x = -12là où ce baril a émis des 2 + 5 + 4 + 19 = 30unités. 2 + 7 + 11 + 30 = 50, qui est la réponse.

Remarques

Il existe une O(N!)solution évidente . Cependant, j'ai exploré des méthodes gourmandes telles que passer à la plus proche ou passer au cluster le plus proche, mais celles-ci n'ont pas fonctionné.

J'ai réfléchi à ce problème pendant un bon moment et je l'ai modélisé comme un problème de recherche de graphique:

  1. Nous insérons 0comme position de base (ce sera l'état initial)
  2. Ensuite, nous trions les positions du plus petit au plus grand.
  3. Nous faisons ensuite un BFS / PFS, où le se statecompose de
    • Deux entiers let rqui représentent une plage contiguë dans le tableau de position trié que nous avons déjà visité
    • Un entier locqui nous indique si nous sommes sur l'extrémité gauche ou droite de la plage
    • Un entier timequi nous indique le temps écoulé
    • Un «coût» entier qui nous indique le coût total jusqu'à présent (basé sur les nœuds que nous avons visités)
  4. De chaque état, nous pouvons passer à [l - 1, r] et [l, r + 1], en ajustant les 3 autres entiers en conséquence
  5. L'état final est [0, N], vérifiant les deux positions finales.

Cependant, il semble que [L, R, loc]ne définit pas uniquement un état, et nous devons stocker L, R, loc, and time, tout en minimisant costà chacun de ces derniers. Cela conduit à un algorithme exponentiel, qui est encore beaucoup trop lent pour tout bien.

Quelqu'un peut-il m'aider à développer mon idée ou à la pousser dans la bonne direction?

Edit: Peut-être que cela peut être modélisé comme un problème d'optimisation de programmation dynamique? En y réfléchissant, il a les mêmes problèmes que la solution de recherche de graphiques - ce n'est pas parce que le courant costest faible que c'est la réponse optimale à ce sous-problème, car cela timeaffecte également considérablement la réponse.

Greedy ne fonctionne pas: j'ai un algorithme de sélection gourmand qui estime le coût du déplacement vers un certain endroit (par exemple, si nous nous déplaçons à droite, nous doublons les distances vers les barils de gauche et autres).

Pourriez-vous faire une recherche prioritaire, avec une heuristique? L'heuristique pourrait combiner le coût du voyage en cours avec le temps écoulé.


Que diriez-vous des algorithmes de chemin le plus court? Comme l'algorithme de Dijkstra?
suraj_fale

J'ai essayé, mais je pense que je fais quelque chose de vraiment mauvais. J'ai décrit mon algorithme (qui était la première recherche prioritaire ou BFS) vers le bas, avec la liste numérotée.
Barron W.

Cela pourrait vous aider ... stackoverflow.com/q/14639346/585398
suraj_fale

Désolé, je ne vois pas comment ces deux problèmes sont liés. Peux-tu expliquer?
Barron W.

2
Il s'agit d'un problème de pratique ACM ICPC, pas un problème réel. Sur une autre note, j'ai essayé de réduire l'état mais en vain. J'ai essayé d'écrire une solution DP mais cela n'a pas fonctionné non plus. L'État était L, R, POS.
Barron W.

Réponses:


4

Je pense que vous pouvez améliorer cela en regardant le problème comme un graphique dirigé de paires de positions.

Pour cet exemple, j'utiliserai la ligne avec les valeurs -9, -6, -1, 3 et 5.

Parce qu'il est trop difficile de dessiner un graphique avec juste du texte, je vais représenter les paires sous forme de tableau. Nous pouvons penser que les cellules représentent l'état où tous les conteneurs entre ces deux positions ont été collectés. Chaque cellule a deux valeurs, l'une représentant le coût pour aller à gauche, l'autre représentant le coût pour aller à droite (jusqu'au conteneur suivant).

Les valeurs peuvent simplement être calculées comme la distance entre les deux points multipliée par le nombre de barils en dehors de ces deux points + 1 . Les cellules où les deux nombres ont le même signe représentent des cas où tous les barils du signe opposé ont été collectés. Ceux-ci sont calculés en utilisant uniquement le nombre de barils dans le sens opposé à zéro.

Dans le tableau, les valeurs de X signifient que vous ne pouvez pas aller dans cette direction (tous les barils dans cette direction ont été pris). Les lignes représentent la position actuelle du collecteur et les colonnes représentent l'emplacement du baril opposé suivant.

    +------+------+------+------+------+
    |  -9  |  -6  |  -1  |   3  |   5  |
+---+------+------+------+------+------+
|-9 |      |      |      | X, 24| X, 14|
+---+------+------+------+------+------+
|-6 | 3, X |      |      | 9, 27| 6, 22|
+---+------+------+------+------+------+
|-1 |      |10, X |      |20, 8 |15, 18|
+---+------+------+------+------+------+
| 3 |24, 4 |27, 6 |16, 8 |      | X, 2 |
+---+------+------+------+------+------+
| 5 |14, X |22, X |18, X |      |      |
+---+------+------+------+------+------+

Les règles de déplacement entre les carrés peuvent être quelque peu déroutantes.

Pour les colonnes numérotées négativement, les règles sont les suivantes:

  • Aller à droite descend d'une cellule.
  • Aller à gauche descend d'une cellule, puis se reflète dans la diagonale.
  • Si la valeur de droite est X, aller vers la gauche monte vers la diagonale (où la colonne et la ligne sont égales) et vers la gauche par un.

Pour les colonnes numérotées positives, les règles sont les suivantes:

  • Aller à gauche remonte d'une cellule.
  • Aller à droite remonte d'une cellule puis se reflète dans la diagonale.
  • Si la valeur de gauche est X, aller à droite descend vers la diagonale et vers la droite de un.

Nous pouvons maintenant exécuter l'algorithme de Dijkstra pour calculer le meilleur chemin, en utilisant ces règles de mouvement pour parcourir le graphique. Nos positions de départ sont (-1, 3) et (3, -1) avec des coûts initiaux de 5 et 15, respectivement. Une fois que nous avons calculé les valeurs pour les deux positions finales (à gauche de (-9, -9) et à droite de (5, 5)), la plus petite des deux est notre réponse.

Le temps d'exécution de chacune des étapes est le suivant:

  • O (N log N) pour trier initialement les valeurs d'entrée le long de la ligne
  • O (N ^ 2) pour calculer le tableau / graphique
  • O (N ^ 2 log N) pour exécuter Dijkstra sur le graphique (Remarque: il y a au plus deux arêtes pour un sommet donné).

Les deux premières étapes sont dominées par la dernière, donc votre temps d'exécution global est O (N ^ 2 log N), ce qui devrait être un temps d'exécution suffisant pour le défi.


1

Distance la plus courte

J'ai écrit une application Java hier pour résoudre le problème. Le problème est essentiellement un problème de distance la plus courte, comme SRJ l'a dit dans son commentaire. Le rayonnement montre simplement que vous pouvez accumuler une valeur avec la distance la plus courte.

En gros, voici ce que j'ai fait.

  • Mettez les numéros de conteneurs dans une liste <Integer>
  • Alors que la liste contient des éléments;
    • Trouvez les éléments les plus proches
    • S'il y a un élément, allez-y et supprimez l'élément.
    • S'il y a deux éléments, copiez le chemin et accédez aux deux éléments
  • Trouvez le chemin avec la plus petite valeur de rayonnement.

Voici quelques sorties de l'application

10 containers are located at [-90, -75, -47, -9, 9, 26, 48, 50, 64, 79].

You walk to position -9 and pick up the container.  The total radiation emitted is 90 units.
You walk to position 9 and pick up the container.  The total radiation emitted is 252 units.
You walk to position 26 and pick up the container.  The total radiation emitted is 388 units.
You walk to position 48 and pick up the container.  The total radiation emitted is 542 units.
You walk to position 50 and pick up the container.  The total radiation emitted is 554 units.
You walk to position 64 and pick up the container.  The total radiation emitted is 624 units.
You walk to position 79 and pick up the container.  The total radiation emitted is 684 units.
You walk to position -47 and pick up the container.  The total radiation emitted is 1,062 units.
You walk to position -75 and pick up the container.  The total radiation emitted is 1,118 units.
You walk to position -90 and pick up the container.  The total radiation emitted is 1,133 units.

Je n'ai optimisé l'algorithme en aucune façon. Je n'ai même pas remarqué que la liste d'entrée des numéros était triée. (Je suis un doofus.)

Lorsque j'ai exécuté mon code avec les valeurs maximales, 1 000 conteneurs et une plage de -500 000 à 500 000, mon code a pris 3 secondes pour s'exécuter. La plupart de ce temps écrivait les 1 000 lignes d'impression sur la console.

Je ne suis pas un grand O, mais je pense que ma force brute parcourant l'algorithme de chemin le plus court est O (N au carré), pas O (N!).

Si j'ai profité du fait que les numéros d'entrée sont triés, de sorte que je n'ai eu qu'à vérifier les deux numéros de chaque côté de l'endroit où je me trouve actuellement, je pourrais faire descendre l'application près de O (N). Je n'ai qu'à vérifier 2 chiffres parce que je supprime les éléments de la liste au fur et à mesure que j'y arrive.

Vous avez utilisé différents algorithmes, comme l'algorithme gourmand, pour trouver une solution approximative.

Si mon programme avait duré 3 heures au lieu de 3 secondes, alors vous auriez le choix.

La bonne solution est-elle assez bonne?

En d'autres termes, suis-je prêt à échanger la vitesse de traitement contre une bonne réponse?

Si une bonne réponse est suffisante, vous utilisez des algorithmes d'approximation.

Si vous voulez la réponse parfaite, vous devez parcourir tous les chemins les plus courts. Il n'y a pas de raccourci.

Si quelqu'un veut que je poste mon code, je le ferai. Je suis sûr qu'il y a encore des bugs, car je voulais voir si je pouvais implémenter un algorithme de marche le plus court.


1

J'ai une solution qui résoudra ce problème à 2^Ntemps, ce qui est médiocre, mais je pense que c'est une façon utile de formuler le problème, alors j'ai pensé que je publierais.

Plutôt que de modéliser le problème sous forme de graphique, je modéliserais qu'il s'agit d'un arbre de décision binaire (disons T ). À chaque niveau, vous devez choisir entre aller à droite ou à gauche. Il est assez facile de calculer le «coût» de chaque bord. Soit h(K)la hauteur du nœud actuel K, puis le coût de l'arête left_child(K) = h(K) x dist(K, left_child(K)). Un calcul similaire suffit pour le coût du bord pour le bon enfant. Vous construisez cet arbre, gardez une trace du coût cumulé des arêtes tout en bas et signalez le chemin d'accès au nœud feuille avec le coût total le plus petit.

Notez que le calcul des coûts fonctionne parce que la longueur de chaque bord dist(K, left_child(K))représente le temps pour aller au site suivant, tandis que la hauteur de la sous-arborescence est le nombre de sites restants (par exemple, émettant toujours un rayonnement).

Maintenant, l'astuce dans ce cadre consiste à déterminer s'il existe des heuristiques que vous pouvez utiliser pour «prouver» que vous pouvez ignorer l'extension de la recherche le long d'une branche. Mon intuition est que pour une telle heuristique, il existe un arrangement de sites qui le vaincra, mais peut-être que quelqu'un peut trouver quelque chose.

Un certain nombre ont proposé d'appliquer des solutions de chemin le plus court pour les graphiques, j'ai quelques doutes quant à savoir si une telle solution peut fonctionner. Vos voisins dans le «graphique» du problème changeront en fonction du chemin que vous suivez. Par exemple dans votre message d'origine avec [-12, -2, 3, 7]si vous allez à -2alors -12et 3devenez «voisins» et si vous allez à 3alors -2et 7êtes voisins. Chaque «paire» possible de valeurs positives et négatives peut potentiellement être des heures voisines dans le graphique final. Je ne connais aucun algorithme de chemin le plus court qui soit prouvablement correct dans un graphique dynamique.


0

Je pense qu'il est plus logique de penser à chaque étape simplement comme un choix binaire entre aller à droite vers le baril le plus proche et aller à gauche vers le baril le plus proche. Ayez simplement une fonction de coût qui détaille le nombre d'unités de rayonnement qui seraient encourues au total en effectuant un mouvement et choisissez celle qui a le coût le plus bas.

Ne considérez PAS simplement le baril le plus proche, mais supposez plutôt qu'en vous éloignant d'un baril, vous ajoutez effectivement deux fois le rayonnement, car en vous éloignant, vous avez également encouru le coût d'avoir à reculer dans cette direction plus tard.

Dans votre exemple de [-12, -2,3,7], un déplacement vers la gauche entraînerait un total de 14 (2 + 2 + 10) à gauche et 18 (2 + 2 + 5 + 9) à droite, pour un total de 22. Déplacer vers la droite entraînerait 10 (3 + 3 + 4) à droite et 26 (3 + 3 + 5 + 15) à droite. Clairement à gauche est la bonne solution au premier abord. Un calcul similaire peut être effectué pour chaque mouvement successif.

Après cela, le problème se réduit essentiellement à une recherche, donc la complexité devrait être O (nlog (n)), ce qui est BEAUCOUP mieux que O (n!). Je pense que c'est nécessairement la plus faible complexité qui peut exister pour ce problème car c'est fondamentalement un algorithme de recherche basé sur la comparaison pour lequel il n'est pas possible de faire mieux que O (nlog (n))

Apparemment, je n'étais pas assez clair avec cette description, j'ai donc décidé de la rendre un peu plus programmatique: 1. Calculer le coût engagé en allant à gauche et le coût engagé en allant à droite en fonction de la position actuelle 2. Déplacer la direction la moins chère 3. Retirez le baril atteint de la considération dans le calcul du coût du déplacement dans une direction

Calcul du coût: 1. Identifiez le baril le plus proche dans la direction donnée. Disons que $ dist est la distance de la position actuelle au baril le plus proche dans la direction donnée. 2. Le coût est initialisé en N * $ dist où N ne considère que les barils encore actifs. 3. À cela, ajoutez la distance que la nouvelle position indiquée par $ dist serait de chaque baril restant.


1
Cela ne fonctionne pas toujours. Peut-être pourriez-vous trier les coordonnées puis faire une recherche où l'état contient une plage [i..j] (qui signifie quelle plage vous avez visitée) et le coût et l'heure actuelle.
Barron W.

Quand cela ne fonctionne-t-il pas?
Slater Victoroff

Il y avait un cas de test simple où cela a échoué. Je vais essayer de le trouver, mais c'était avec N = 4 ou 5.
Barron W.

[43, -18, -98, -82, 63]
Barron W.

Des cas comme [-10,-11, 10,20,30,40,50,60,70]. La seule solution correcte est de collecter tous les négatifs puis de collecter les positifs. Pour une réponse de 455.
Barron W.

0

Solution partielle - j'y reviendrai plus tard.

Supposons que la stratégie "par défaut" soit exécutée complètement à gauche ou à droite, selon ce qui est le moins cher. Maintenant demandez, vaut-il un petit détour dans l'autre sens pour ramasser un baril. Il est assez facile de calculer la réponse.

Pour votre échantillon d'entrée, courir à droite est moins cher qu'à gauche. Est-ce que aller à -2 vaut un détour? Cela réduit le coût de la course à droite puis de nouveau à 0 par 14 (parce que vous "payiez" 4 unités de rayonnement par coup de 0 à 3 sur la stratégie par défaut, maintenant c'est à 3, vous en payiez 3 sur 3 à 7, maintenant c'est 2, etc.), plus réduit de 1 par coup votre coût de déplacement de 0 à -2, économisant ainsi 2 de plus pour un total de 16.

Cependant, cela ajoute un coût pour aller à -2 puis revenir à 0 sur 14 (4 unités par coup à -2, 3 par coup à 0), pour un gain net de (16-14) = 2. Notez que pour calculer cela, vous n'avez pas à évaluer le coût exact de la résolution de tout le problème pour chaque décision - vous avez suffisamment d'informations pour décider simplement en sachant si courir à gauche est moins cher que courir à droite, plus comment de nombreux conteneurs de déchets sont de chaque côté de vous, et la distance au plus proche 2. C'est donc O (N ^ 2).

À l'exception d'un problème important - j'ai supposé que vous courrez jusqu'à la fin, et nous savons que vous pourriez doubler. Pour nettoyer cela, nous devons mettre à jour mon calcul. Pour l'échantillon d'entrée, j'ai supposé que vous économiseriez 14 en émettant 1 rayonnement total de moins par unité par seconde tout en passant de 0 à 7 et inversement. Mais, si vous doublez avant de courir à 7, les économies seront réduites.

C'est assez mauvais, car je ne sais pas calculer le prochain double-back sans essayer toutes les possibilités, ce qui nous ramène à O (2 ^ N).

Sauf - c'est 2 ^ N avec l'élagage. J'ai calculé que le "voyage parallèle" à -2 coûtait 14, mais j'en ai gagné 16, si je n'avais pas eu plus de voyages secondaires avant d'arriver au nombre le plus à droite. Si le nombre le plus à droite avait été 5, je saurais immédiatement que le voyage parallèle à -2 ne pouvait pas porter ses fruits. (Coût encore 14, avantage maximum 12). Je ne dois pas non plus envisager d'aller à -2 puis de faire un détour avant d'atteindre 6, car c'est toujours inférieur à aller directement à ce point en premier lieu.


0

Je pense que vous pouvez le résoudre en utilisant une recherche en largeur, en ne conservant pas plus de 2 * N ^ 2 tuples de (booléen, int, int, int, chaîne) où les chaînes sont aussi longues que le chemin est compliqué.

Les tuples sont (booléens min ou max, position min parcourue, position max parcourue, rayonnement total émis, historique du trajet).

Je vois l'algorithme comme ceci:

  1. Initialiser le pool de tuples en une seule entrée, (min, 0, 0, 0, "")
  2. Trouvez un élément dans la piscine qui émet un minimum de rayonnement. Si le min et le max correspondent au min et au max de tous les barils, alors l'historique du trajet est la solution optimale. Sinon, supprimez-le du pool.
  3. Calculez les 2 descendants de ce tuple, chacun correspondant à une marche à gauche ou à droite vers le baril non traité suivant.
  4. Insérez les descendants dans le pool. S'il y a déjà un élément dans le pool avec le même booléen, min et max qu'un nouveau descendant, alors jetez l'élément avec le plus grand nombre de radiations.
  5. goto 2.

La recherche et la suppression de tuples dominés amélioreront considérablement les performances. Il pourrait être utile d'ajouter un indicateur «a élevé» à chaque tuple et de laisser des tuples élevés dans la piscine.

Il y a aussi des décisions importantes à prendre pour décider comment stocker les tuples et les rechercher pour les dominations et les nouveaux éléments à reproduire.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.