Problème de citrouille itinérante


23

Contexte:

Jack est une citrouille qui aime effrayer les citoyens des villages près de son potiron à chaque Halloween. Cependant, chaque année après que quelqu'un a allumé la bougie à l'intérieur de lui, il a un temps limité pour effrayer tout le monde avant que la bougie ne s'éteigne, ne pouvant ainsi effrayer plus de villageois parce que personne ne peut le voir. Ces dernières années, il n'a pu effrayer qu'un petit nombre de villages en raison de sa mauvaise prise de décision, mais maintenant qu'il vous a aidé, il pourra effrayer autant de villages que possible!

Tâche:

Compte tenu de la liste des emplacements des villages et de la durée de vie des bougies, affichez le nombre maximal de villages que Jack peut visiter. Vous n'avez pas besoin d'imprimer le chemin lui-même.

Contribution:

La durée de vie de la bougie et une liste des emplacements des villages dans un système de coordonnées cartésiennes. Le patch de citrouille d'origine de Jack sera toujours à 0,0. Vous pouvez formater l'entrée comme vous le souhaitez. Pour simplifier les mouvements de Jack, il ne peut se déplacer qu'horizontalement, verticalement ou en diagonale, ce qui signifie que sa bougie perdra 1 ou 1,5 (il prend un peu plus longtemps en diagonale) unités de vie à chaque mouvement. La bougie s'éteint lorsque la durée de vie est inférieure ou égale à 0.

Sortie:

Un entier égal au nombre maximum de villages que Jack peut visiter avant que la bougie ne s'éteigne.

Règles:

C'est le , donc le code le plus court en octets gagne. Les échappatoires standard ne sont pas autorisées.

Cas de test:

// Format [lifespan] [list of village coordinates] -> [maximum visit-able villages]

4 -1,0 1,0 2,0 3,0 4,0 5,0 -> 3
4 1,1 2,2 3,3 -> 2
5 1,1 2,1 3,1 4,1 5,0 5,1 -> 4

9
Rire du titre
Luis Mendo

3
"Pour simplifier les mouvements de Jack" est un peu ironique, c'est beaucoup plus difficile maintenant: D
PurkkaKoodari

1
Je pense que votre première sortie de cas devrait être 3 si je ne me trompe pas
Numberknot

1
@Numberknot Non, une fois qu'un village a eu peur, il ne tombera pas dans le même piège, il ne peut effrayer chaque village qu'une seule fois.
Yodle

5
Il s'agit d'un problème N-Pumpkin Hard, donc en général le nombre maximum de villages peut être difficile à trouver. Il y a un nombre maximum de villages?
edc65

Réponses:


9

Gelée, 30 29 27 25 octets

_AṢæ..
0,0ṭṚç2\+\<S
Œ!ç€Ṁ

Essayez-le en ligne!

Apparemment, le produit scalaire de Jelly ignore simplement une incompatibilité de taille de liste et ne multiplie pas les éléments supplémentaires de l'autre tableau, les ajoute simplement. Rase 2 octets.

Explication

_AṢæ..              Helper link to calculate distance. Arguments: a, b
_                     subtract the vertices from each other
 A                    take absolute values of axes
  Ṣ                   sort the axes
   æ..                dot product with [0.5]

0,0ṭṚç2\+\<S        Helper link to calculate max cities. Arguments: perm, max
0,0                   create pair [0,0]
   ṭ                  append that to the permutation
    Ṛ                 reverse the permutation (gets the [0,0] to the beginning)
     ç2\              find distances of each pair using the previous link
        +\            find all partial sums
          <           see if each sum was less than the max
           S          sum to count cases where it was

Œ!ç€Ṁ               Main link. Arguments: cities, max
Œ!                    get permutations of cities
  ç€                  find max cities for each permutation using the previous link
    Ṁ                 take the maximum

Dans un commentaire, OP demande de gérer jusqu'à 1000 villages. Mais toute réponse qui génère et stocke toutes les permutations échouera même dans 15 villages (~ 1300 milliards de permutations)
edc65

@ edc65 Nulle part cela ne dit que les cas aussi gros doivent être testables, tant que l'algorithme fonctionne théoriquement avec suffisamment de temps et de mémoire. (Les programmes qui peuvent réellement résoudre le TSP pour n ≈ 1000 sont si complexes qu'ils ne seraient plus amusants pour le golf.)
PurkkaKoodari

Ok pas 1000, mais même pas 15?
edc65

@ edc65 Je ne trouve pas d'algorithme qui serait rapide et aurait l' air facilement implémentable dans Jelly. Je pourrais envisager de créer une solution plus efficace (par exemple Held-Karp) dans une autre langue. BTW, aucune des réponses n'utilise des algorithmes réellement rapides; celle de JS est meilleure, mais lente s'il y a beaucoup de villes à portée.
PurkkaKoodari

5

Java 7, 206 201 octets

Merci à @KevinCruijssen pour avoir économisé 5 octets

int f(float e,int[]a,int[]b){int x=0,y=0,c=0,d=0,t;float s;for(int i:a){s=(i!=x&b[c]==y)|(i==x&b[c]!=y)?Math.sqrt((t=i-x)*t+(t=b[c]-y)*t)*1:Math.abs(i-x)*1.5;d+=e-s>=0?1:0;e-=s;x=i;y=b[c++];}return d;}

Non golfé

class Travellingpumpkin {

public static void main(String[] args) {

    System.out.println(f( 5 ,new int[] { 1,2,3,4,5,5 } , new int[] { 1,1,1,1,0,1 } ));

}
static int f( double e , int[]a , int[]b ) {
    int x = 0 , y = 0 , c = 0 , d = 0 , t;
    double s ;

    for ( int i : a ) {
    s = ( i != x & b[c] == y )|( i == x & b[c] != y )
         ? Math.sqrt( ( t = i - x ) * t + ( t = b[c] - y ) * t ) * 1
         : Math.abs( i - x ) * 1.5 ;


        d += e-s >= 0 ? 1 : 0 ;
        e -= s ;
        x = i ; y = b [ c++ ] ;
    }
    return d ;

}

   }

2
Agréable, bon pour inclure la forme "non golfée". Bien que si vous transformiez cela, je pense que le réviseur de code ne l' appellerait pas non golfé. ;)
Wildcard

+1. Une chose au golf: vous utilisez i-xdeux fois et b[c]-ydeux, vous pouvez donc ajouter ,taux pouces, puis utiliser ceci: Math.sqrt((t=i-x)*t+(t=b[c]-y)*t)*1au lieu de Math.sqrt((i-x)*(i-x)+(b[c]-y)*(b[c]-y))*1.
Kevin Cruijssen du

Comment cela pourrait-il fonctionner dans le cas général?
edc65

3

Scala, 196 octets

def f(l:Int,c:(Int,Int)*)=c.permutations.map(x=>((0,0)+:x sliding 2 map{p=>val Seq(c,d)=Seq((p(0)._1-p(1)._1)abs,(p(0)._2-p(1)._2)abs).sorted
c*1.5+(d-c)}scanLeft 0d)(_+_)takeWhile(_<l)size).max-1

Non golfé:

def g (l: Int, c: (Int, Int)*) = {
    c.permutations
    .map { x =>
        ((0, 0) +: x).sliding(2).map({ p =>
            val Seq(c, d) = Seq((p(0)._1 - p(1)._1) abs, (p(0)._2 - p(1)._2) abs).sorted
            c * 1.5 + (d - c)
        }).scanLeft(0d)(_ + _).takeWhile(_ < l).size
    }.max - 1
}

Explication:

def f(l:Int,c:(Int,Int)*)= //defien a function with an int and a vararg-int-pait parameter
  c.permutations           //get the permutations of c, that is all possible routes
  .map(x=>                 //map each of them to...
    ((0,0)+:x                //prepend (0,0)
    sliding 2                //convert to a sequence of consecutive elemtens
    map{p=>                  //and map each of them to their distance:
      val Seq(c,d)=Seq(        //create a sequence of
        (p(0)._1-p(1)._1)abs,  //of the absolute distance between the x points
        (p(0)._2-p(1)._2)abs   //and he absolute distance between the y coordinates
      ).sorted                 //sort them and assign the smaller one to c and the larger one to d
      c*1.5+(d-c)              //we do the minimum difference diagonally
    }                        //we now have a sequence of sequence of the distances for each route
    scanLeft 0d)(_+_)       //calculate the cumulative sum
    takeWhile(_<l)          //and drop all elements that are larger than the candle lifespan
    size                    //take the size
  ).max-1                   //take the maximum, taht is the size of the largest route and subtract 1 because we added (0,0) at the beginning

3

JavaScript (ES6), 145

Fonction récursive anonyme, le paramètre sest la durée de vie de la bougie, le paramètre lest la liste des coordonnées du village.

A Depth First Search , s'arrêtant lorsque la distance atteint la durée de vie de la bougie

f=(s,l,x=0,y=0,v=0,A=Math.abs,X=Math.max)=>X(v,...l.map(([t,u],i,[h,...l],q=A(t-x),p=A(u-y),d=(l[i-1]=h,p+q+X(p,q))/2)=>s<=d?v:f(s-d,l,t,u,1+v)))

Moins de golf voir l'extrait ci-dessous

Tester

f=(s,l,x=0,y=0,v=0,A=Math.abs,X=Math.max)=>
  X(v,...l.map(
      ([t,u],i,[h,...l],q=A(t-x),p=A(u-y),d=(l[i-1]=h,p+q+X(p,q))/2)=>
      s<=d?v:f(s-d,l,t,u,1+v)
  ))

// ungolfed version

F=(s, l, 
   x=0, y=0, // current position
   v=0 // current number of visited sites 
  ) =>
   Math.max(v, ...l.map(
     (
       [t,u], i, [h,...l], // lambda arguments
       q = Math.abs(t-x), p = Math.abs(u-y), // locals
       d = (p+q+Math.max(p,q))/2
     ) => (
       l[i-1] = h,
       s <= d 
         ? v 
         : F(s-d, l, t, u, v+1)
     ) 
  ))

;[[4,[[-1,0],[1,0],[2,0],[3,0],[4,0],[5,0]], 3]
,[4, [[1,1],[2,2],[3,3]], 2]
,[5, [[1,1],[2,1],[3,1],[4,1],[5,0],[5,1]], 4]
].forEach(test=>{
  var span=test[0],list=test[1],check=test[2],
      result = f(span, list)
  console.log(result==check?'OK':'KO',span, list+'', result)
})


3

MATL , 27 octets

EH:"iY@OwYc!d|]yyXl++Ys>sX>

EDIT (26 nov 2016): En raison de changements dans la Xlfonction, elle doit être remplacée dans le code ci-dessus par 2$X>. Les liens ci-dessous intègrent cette modification.

Essayez-le en ligne! Ou vérifiez tous les cas de test .

Explication

La distance citrouille entre deux villes séparées Δ x et Δ y dans chaque coordonnée peut être obtenue comme (| Δ x | + | Δ y | + max (| Δ x |, | Δ y |)) / 2.

Le code suit ces étapes:

  1. Générez toutes les permutations des coordonnées x et des coordonnées y et ajoutez un a à chaque 0. Chaque permutation représente un chemin possible.
  2. Calculez les différences consécutives absolues pour chaque chemin (ce sont | Δ x | et | Δ y | ci-dessus).
  3. Obtenez la distance citrouille pour chaque étape de chaque chemin.
  4. Calculez la somme cumulée des distances pour chaque chemin.
  5. Trouvez, pour chaque chemin, le nombre d'étapes avant que la distance cumulée n'atteigne la durée de vie du chandelier.
  6. Prenez le maximum de ce qui précède.

Code commenté:

E        % Input candle lifespan implicitly. Multiply by 2
H:"      % Do thie twice
  i      %   Input array of x or y coordinates
  Y@     %   All permutations. Gives a matrix, with each permutation in a row
  OwYc   %   Prepend a 0 to each row
  !      %   Transpose
  d|     %   Consecutive differences along each column. Absolute value
]        % End
yy       % Duplicate the two matrices (x and y coordinates of all paths)
Xl       % Take maximum between the two, element-wise
++       % Add twice. This gives twice the pumpkin distance
Ys       % Cumulative sum along each column
>        % True for cumulative sums that exceed twice the candle lifespan
s        % Sum of true values for each column
X>       % Maximum of the resulting row array. Inmplicitly display

MATL peut-il vraiment générer toute permutation de 1000 paires (x, y)?
edc65

@ edc65 Non, c'est trop (il y a plus de 10 ^ 2500 permutations de 1000 éléments). Je ne pense pas qu'une langue puisse le faire
Luis Mendo

Dans un commentaire, OP demande de gérer jusqu'à 1000 villages. Mais toute réponse qui génère et stocke toutes les permutations échouera même dans 15 villages (~ 1300 milliards de permutations)
edc65

@ edc65 Ah, je vois. 1000 villages semblent irréalistes si le problème est NP-difficile, comme il semble l'être
Luis Mendo

2

Python 2.7 , 422 octets

merci à NoOneIsHere d'avoir signalé des améliorations supplémentaires!

merci à edc65 d'avoir noté de ne pas sauvegarder la liste mais d'utiliser des itérateurs à la place!

Essayez-le en ligne!

from itertools import permutations
def d(s,e):
    d=0
    while s!=e:
        x=1 if s[0]<e[0] else -1 if s[0]>e[0] else 0
        y=1 if s[1]<e[1] else -1 if s[1]>e[1] else 0
        s=(s[0]+x,s[1]+y)
        d+=(1,1.5)[x and y]
return d
l,m=4,0
for o in permutations([(1,1),(2,2),(3,3)]):
    a,c=l-d((0,0),o[0]),1
    for j in range(len(o)-1):
        a-=d(o[j],o[j+1])
        c+=(0,1)[a>0]
    m=max(c,m)
print m

Explication:

La fonction calcule la distance entre deux points selon les règles données, la boucle parcourt toutes les permutations générées par le générateur de l'entrée et calcule la distance, si la distance est inférieure à la durée de vie de la bougie, elle la soustrait et ajoute la place à la compteur, si ce compteur est supérieur au courant max, il le remplace.

non golfé:

from itertools import permutations

def distance(start_pos, end_pos):
    distance = 0
    while start_pos != end_pos:
        mod_x = 1 if start_pos[0] < end_pos[0] else -1 if start_pos[0] > end_pos[0] else 0
        mod_y = 1 if start_pos[1] < end_pos[1] else -1 if start_pos[1] > end_pos[1] else 0
        start_pos = (start_pos[0] + mod_x, start_pos[1] + mod_y)
        distance += (1, 1.5)[mod_x and mod_y]
    return distance

lifespan, max_amount = 4, 0
for item in permutations([(1,1), (2,2), (3,3)]):
    lifespan_local, current = lifespan - distance((0,0), item[0]), 1
    for j in range(len(item) - 1):
        lifespan_local -= distance(item[j], item[j + 1])
        current += (0, 1)[lifespan_local > 0]
    max_amount = max(current, max_amount)
print max_amount

Bonjour et bienvenue chez PPCG! Vous pouvez faire current c, et ll m.
NoOneIsHere

Ouah merci! manqué celui-là
Gmodjackass

Dans un commentaire, OP demande de gérer jusqu'à 1000 villages. Mais toute réponse qui génère et stocke toutes les permutations échouera même dans 15 villages (~ 1300 milliards de permutations)
edc65

examinera cela à un moment donné, merci pour la tête. Je n'ai pas vraiment lu les commentaires car ils sont nombreux.
Gmodjackass

fait, en utilisant le générateur maintenant, au lieu de stocker toutes les permutations qu'il les génère sur le pouce, devrait utiliser environ O (n) pour la permutation.
Gmodjackass

1

PHP, 309 octets

function j($x,$y,$c,$v){if($s=array_search([$x,$y],$v))unset($v[$s]);for($c--,$i=4;$c>0&&$i--;)$m=($n=j($x+[1,0,-1,0][$i],$y+[0,1,0,-1][$i],$c,$v))>$m?$n:$m;for($c-=.5,$i=4;$c>0&&$i--;)$m=($n=j($x+[1,-1,-1,1][$i],$y+[1,1,-1,-1][$i],$c,$v))>$m?$n:$m;return$s?++$m:$m;}echo j(0,0,$argv[1],array_chunk($argv,2));

Absolument brutale et même pas très courte. Utilisez comme:

php -r "function j($x,$y,$c,$v){if($s=array_search([$x,$y],$v))unset($v[$s]);for($c--,$i=4;$c>0&&$i--;)$m=($n=j($x+[1,0,-1,0][$i],$y+[0,1,0,-1][$i],$c,$v))>$m?$n:$m;for($c-=.5,$i=4;$c>0&&$i--;)$m=($n=j($x+[1,-1,-1,1][$i],$y+[1,1,-1,-1][$i],$c,$v))>$m?$n:$m;return$s?++$m:$m;}echo j(0,0,$argv[1],array_chunk($argv,2));" 5 1 1 2 1 3 1 4 1 5 0 5 1

Avec plus d'espace et enregistré dans un fichier:

<?php 
function j( $x, $y, $c, $v)
{
    if( $s = array_search( [$x,$y], $v ) )
        unset( $v[$s] );

    for( $c--, $i=4; $c>0 && $i--;)
        $m = ( $n=j($x+[1,0,-1,0][$i],$y+[0,1,0,-1][$i],$c,$v) )>$m ? $n : $m;

    for( $c-=.5, $i=4; $c>0 && $i--;)
        $m = ( $n=j($x+[1,-1,-1,1][$i],$y+[1,1,-1,-1][$i],$c,$v) )>$m ? $n : $m;

    return $s ? ++$m : $m;
}
echo j( 0, 0, $argv[1], array_chunk($argv,2) );

1

Python, 175 octets

def f(c,l):
 def r(t):p=abs(t[0]-x);q=abs(t[1]-y);return p+q-.5*min(p,q)
 v=0;x,y=0,0
 while c>0 and len(l)>0:
  l.sort(key=r);c-=r(l[0]);x,y=l.pop(0)
  if c>=0:v+=1
 return v

cest la durée de vie de la bougie, lest une liste de tuples - coordonnées du village, vest le nombre de villages visités, (x,y)est la paire de coordonnées du village où Jack se trouve actuellement.

r(t)est une fonction qui calcule la distance jusqu'à la position actuelle et est utilisée pour trier la liste afin que la plus proche devienne l[0]. La formule utilisée est | Δx | + | Δy | - min (| Δx |, | Δy |) / 2.

Essayez-le ici!


1

Raquette

(define (dist x1 y1 x2 y2)     ; fn to find distance between 2 pts
  (sqrt(+ (expt(- x2 x1)2)
          (expt(- y2 y1)2))))

(define (fu x1 y1 x2 y2)       ; find fuel used to move from x1 y1 to x2 y2;
  (let loop ((x1 x1)
             (y1 y1)
             (fuelUsed 0))
    (let* ((d1 (dist (add1 x1) y1 x2 y2))        ; horizontal movement
           (d2 (dist x1 (add1 y1) x2 y2))        ; vertical movement
           (d3 (dist (add1 x1) (add1 y1) x2 y2)) ; diagonal movement
           (m (min d1 d2 d3))) ; find which of above leads to min remaining distance; 
      (cond 
        [(or (= d2 0)(= d1 0)) (add1 fuelUsed)]
        [(= d3 0) (+ 1.5 fuelUsed)]
        [(= m d1) (loop (add1 x1) y1 (add1 fuelUsed))]
        [(= m d2) (loop x1 (add1 y1) (add1 fuelUsed))]
        [(= m d3) (loop (add1 x1) (add1 y1) (+ 1.5 fuelUsed))]))))

(define (f a l)
  (define u (for/list ((i l))
    (fu 0 0 (list-ref i 0) (list-ref i 1))))  ; find fuel used for each point; 
  (for/last ((i u)(n (in-naturals)) #:final (>= i a))
    n))

Essai:

(f 4 '((1 1) (2 2) (3 3))) ;-> 2
(f 5 '((1 1) (2 1) (3 1) (4 1) (5 0) (5 1))) ;-> 4

Sortie:

2
4

Cependant, le code ci-dessus ne fonctionne pas pour les valeurs négatives de x et y.

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.