Ordina alcune mele!


11

Problema

Immagina 7 secchi allineati in fila. Ogni secchio può contenere al massimo 2 mele. Ci sono 13 mele etichettate da 1 a 13. Sono distribuite tra i 7 secchi. Per esempio,

{5,4}, {8,10}, {2,9}, {13,3}, {11,7}, {6,0}, {12,1}

Dove 0 rappresenta lo spazio vuoto. L'ordine in cui le mele compaiono all'interno di ogni secchio non è rilevante (ad esempio {5,4} è equivalente a {4,5}).

Puoi spostare qualsiasi mela da un secchio a un secchio adiacente, a condizione che ci sia spazio nel secchio di destinazione per un'altra mela. Ogni mossa è descritta dal numero della mela che si desidera spostare (il che è inequivocabile perché c'è solo uno spazio vuoto). Ad esempio, applicando la mossa

7

alla disposizione di cui sopra si tradurrebbe in

{5,4}, {8,10}, {2,9}, {13,3}, {11,0}, {6,7}, {12,1}

Obbiettivo

Scrivere un programma che legge una disposizione da STDIN e la ordina nella seguente disposizione

{1,2}, {3,4}, {5,6}, {7,8}, {9,10}, {11,12}, {13,0}

usando il minor numero di mosse possibile. Ancora una volta, l'ordine in cui le mele compaiono all'interno di ciascun secchio non è rilevante. L'ordine dei secchi è importante. Dovrebbe produrre le mosse utilizzate per ordinare ciascuna disposizione separata da virgole. Per esempio,

13, 7, 6, ...

Il tuo punteggio è uguale alla somma del numero di mosse richieste per risolvere le seguenti disposizioni:

{8, 2}, {11, 13}, {3, 12}, {6, 10}, {4, 0}, {1, 7}, {9, 5}
{3, 1}, {6, 9}, {7, 8}, {2, 11}, {10, 5}, {13, 4}, {12, 0}
{0, 2}, {4, 13}, {1, 10}, {11, 6}, {7, 12}, {8, 5}, {9, 3}
{6, 9}, {2, 10}, {7, 4}, {1, 8}, {12, 0}, {5, 11}, {3, 13}
{4, 5}, {10, 3}, {6, 9}, {8, 13}, {0, 2}, {1, 7}, {12, 11}
{4, 2}, {10, 5}, {0, 7}, {9, 8}, {3, 13}, {1, 11}, {6, 12}
{9, 3}, {5, 4}, {0, 6}, {1, 7}, {12, 11}, {10, 2}, {8, 13}
{3, 4}, {10, 9}, {8, 12}, {2, 6}, {5, 1}, {11, 13}, {7, 0}
{10, 0}, {12, 2}, {3, 5}, {9, 11}, {1, 13}, {4, 8}, {7, 6}
{6, 1}, {3, 5}, {11, 12}, {2, 10}, {7, 4}, {13, 8}, {0, 9}

Sì, ognuna di queste disposizioni ha una soluzione.

Regole

  • La soluzione deve essere eseguita in tempo polinomiale nel numero di bucket per mossa. Il punto è usare l'euristica intelligente.
  • Tutti gli algoritmi devono essere deterministici.
  • In caso di pareggio, vince il conteggio dei byte più corto.

2
Qual è il punto di indicare la destinazione quando c'è solo uno spazio in cui puoi spostare una mela?
John Dvorak,

Che cosa succede se la mia soluzione a forza bruta funziona in un ragionevole lasso di tempo? Esistono solo 700 milioni di stati, facilmente enumerabili in pochi minuti. Definire "ragionevole quantità di tempo".
John Dvorak,

@JanDvorak Per "Qual è il punto" - buona chiamata. Non mi era venuto in mente. Sto definendo ragionevole qui per essere inferiore alla quantità di tempo richiesto per forzare la soluzione;)
Orby,

La tua definizione di "ragionevole" significa che dovremmo prima implementare la soluzione della forza bruta, quindi tutto ciò che è più veloce conta?
John Dvorak,

L'ordine finale del bucket è importante?
AMK,

Risposte:


4

Punteggio: 448

La mia idea è di ordinarli consecutivamente, a partire da 1. Questo ci dà la bella proprietà che quando vogliamo spostare lo spazio nel cestino precedente / successivo, sappiamo esattamente quale delle due mele ci dobbiamo spostare - il massimo / uno minimo, rispettivamente. Ecco la ripartizione del test:

#1: 62     #6: 40
#2: 32     #7: 38
#3: 46     #8: 50
#4: 50     #9: 54
#5: 40    #10: 36

Total score: 448 moves

Il codice può essere golfato molto di più, ma una migliore qualità del codice motiverà ulteriori risposte.

C ++ (501 byte)

#include <cstdio>
#define S(a,b) a=a^b,b=a^b,a=a^b;
int n=14,a[14],i,j,c,g,p,q;
int l(int x){for(j=0;j<n;++j)if(a[j]==x)return j;}
int sw(int d){
    p=l(0);q=p+d;
    if(a[q]*d>a[q^1]*d)q^=1;
    printf("%d,", a[q]);
    S(a[q],a[p])
}
int main(){
    for(;j<n;scanf("%d", a+j),j++);
    for(;++i<n;){
        c=l(i)/2;g=(i-1)/2;
        if(c-g){
            while(l(0)/2+1<c)sw(2);
            while(l(0)/2>=c)sw(-2);
            while(l(i)/2>g){sw(2);if(l(i)/2>g){sw(-2);sw(-2);}}
        }
    }
}

Ulteriori miglioramenti potrebbero essere il passaggio a C e il tentativo di abbassare il punteggio partendo dai grandi valori verso il basso (e alla fine combinando entrambe le soluzioni).


1
Una sottostringa del codice costituisce già un programma C. In particolare, potrebbe funzionare in C semplicemente eliminando la prima riga.
feersum,

@feersum Hai ragione. All'inizio avevo più codice specifico per C ++, ma dopo quello con il passaggio a C in mente, sembra che me ne sia liberato.
yasen,

Potresti specificare che hai modificato il formato di input nella tua soluzione per renderlo più chiaro a coloro che provano a verificarlo?
Orby,

2

C, 426 448

Questo ordina le mele una alla volta da 1 a 13 simili al metodo di Yasen , tranne quando ha l'opportunità di spostare un numero più grande su o un numero più piccolo giù, lo prenderà. Purtroppo, questo migliora solo le prestazioni del primo problema di test, ma è un piccolo miglioramento. Ho fatto un errore durante l'esecuzione dei problemi del test. Sembra che abbia semplicemente reimplementato il metodo di Yasen.

#1: 62    #6: 40
#2: 32    #7: 38
#3: 46    #8: 50
#4: 50    #9: 54
#5: 40    #10: 36

Richiede input senza parentesi graffe o virgole, ad es

8 2 11 13 3 12 6 10 4 0 1 7 9 5

Ecco il codice golfing che arriva a 423 byte contando alcune newline inutili (probabilmente potrebbe essere golfato di più, ma mi aspetto che questo punteggio venga battuto):

#define N 7
#define F(x,y) for(y=0;y<N*2;y++)if(A[y]==x)break;
#define S(x,y) x=x^y,y=x^y,x=x^y;
#define C(x,y) ((A[x*2]==y)||(A[x*2+1]==y))
A[N*2],i,j,d,t,b,a,n,s,v,u,w,g;main(){for(;i<N*2;i++)scanf("%d",A+i);g=1;while
(!v){F(0,i);b=i/2;F(g,u);w=u/2;d=b<w?1:-1;n=(b+d)*2;a=(b+d)*2+1;if(A[n]>A[a])
S(n,a);t=d-1?a:n;printf("%d,",A[t]);S(A[i],A[t]);while(C((g-1)/2,g))g++;v=1;for
(j=0;j<N*2;j++)if(!C(j/2,(j+1)%(N*2)))v=0;}}

E il codice non golfato, che stampa anche lo spartito:

#include <stdio.h>
#include <stdlib.h>

#define N 7

int apples[N*2];

int find(int apple)
{
    int i;
    for (i = 0; i < N*2; i++) {
        if (apples[i] == apple)
            return i;
    }    
}

void swap(int i, int j)
{
    int temp;
    temp = apples[i];
    apples[i] = apples[j];
    apples[j] = temp;
}

int contains(int bucket, int apple)
{
    if ((apples[bucket * 2] == apple) || (apples[bucket * 2 + 1] == apple))
        return 1;
    return 0;
}

int is_solved()
{
    int i, j;
    for (i = 0; i < N * 2; i++) {
        j = (i + 1) % (N * 2);
        if (!contains(i / 2, j))
            return 0;
    }
    return 1;
}

int main()
{
    int i, j, dir, bucket, max, min, score;
    int target_i, target_bucket, target;

    /* Read the arrangement */
    for (i = 0; i < N*2; i++) {
        scanf("%d ", apples + i);
    }

    target = 1;
    while (1) {

        i = find(0);
        bucket = i / 2;
        target_i = find(target);
        target_bucket = target_i / 2;

        /* Change the direction of the sort if neccesary */
        if (bucket < target_bucket) dir = 1;
        else dir = -1;

        /* Find the biggest and smallest apple in the next bucket */
        if (apples[(bucket + dir) * 2] < apples[(bucket + dir) * 2 + 1]) {
            min = (bucket + dir) * 2;
            max = (bucket + dir) * 2 + 1;
        } else {
            min = (bucket + dir) * 2 + 1;
            max = (bucket + dir) * 2;
        }

        /* If we're going right, move the smallest apple. Otherwise move the
           biggest apple */
        if (dir == 1) {
            printf("%d, ", apples[min]);
            swap(i, min);
            score++;
        } else {
            printf("%d, ", apples[max]);
            swap(i, max);
            score++;
        }

        /* Find the next apple to sort */
        while (contains((target - 1) / 2, target))
            target++;

        /* If we've solved it then quit */
        if (is_solved())
            break;
    }
    printf("\n");
    printf("%d\n", score);
}

2

Python 3 - 121

Questo implementa una ricerca approfondita prima con profondità crescente fino a trovare una soluzione. Utilizza un dizionario per memorizzare gli stati visitati in modo da non visitarli di nuovo se non con una finestra di maggiore profondità. Quando decide quali stati verificare, utilizza come euristico il numero di elementi fuori posto e visita solo gli stati migliori possibili. Si noti che poiché l'ordine degli elementi all'interno del loro bucket non ha importanza, mantiene sempre un ordine all'interno dei bucket. Ciò semplifica il controllo se un elemento non è posizionato correttamente.

L'input è una matrice di ints, con il primo int il numero di bucket.

Ad esempio, per # 8 (questo richiede molto tempo per funzionare sulla mia macchina, gli altri finiscono in pochi secondi):

c:\python33\python.exe apples.py 7 3 4 10 9 8 12 2 6 5 1 11 13 7 0

Ecco i risultati sul set di test: # 1: 12, # 2: 12, # 3: 12, # 4: 12, # 5: 11, # 6: 11, # 7: 10, # 8: 14, # 9: 13, # 10: 14

Ecco il codice:

import sys    

BUCKETS = int(sys.argv[1])    

# cleans a state up so it is in order
def compressState(someState):
  for i in range(BUCKETS):
    if(someState[2*i] > someState[2*i + 1]):
      temp = someState[2*i]
      someState[2*i] = someState[2*i + 1]
      someState[2*i + 1] = temp
  return someState    

state = compressState([int(x) for x in sys.argv[2:]])
print('Starting to solve', state)
WINNINGSTATE = [x for x in range(1, BUCKETS*2 - 1)]
WINNINGSTATE.append(0)
WINNINGSTATE.append(BUCKETS*2 - 1)
maxDepth = 1
winningMoves = []
triedStates = {}    

# does a depth-first search
def doSearch(curState, depthLimit):
  if(curState == WINNINGSTATE):
    return True
  if(depthLimit == 0):
    return False
  myMoves = getMoves(curState)
  statesToVisit = []
  for move in myMoves:
    newState = applyMove(curState, move)
    tns = tuple(newState)
    # do not visit a state again unless it is at a higher depth (more chances to win from it)
    if(not ((tns in triedStates) and (triedStates[tns] >= depthLimit))):
      triedStates[tns] = depthLimit
      statesToVisit.append((move, newState[:], stateScore(newState)))
  statesToVisit.sort(key=lambda stateAndScore: stateAndScore[2])
  for stv in statesToVisit:
    if(stv[2] > statesToVisit[0][2]):
      continue
    if(doSearch(stv[1], depthLimit - 1)):
      winningMoves.insert(0, stv[0])
      return True
  return False    

# gets the moves you can make from a given state
def getMoves(someState):
  # the only not-allowed moves involve the bucket with the 0
  allowedMoves = []
  for i in range(BUCKETS):
    if((someState[2*i] != 0) and (someState[2*i + 1] != 0)):
      allowedMoves.append(someState[2*i])
      allowedMoves.append(someState[2*i + 1])
  return allowedMoves    

# applies a move to a given state, returns a fresh copy of the new state
def applyMove(someState, aMove):
  newState = someState[:]
  for i in range(BUCKETS*2):
    if(newState[i] == 0):
      zIndex = i
    if(newState[i] == aMove):
      mIndex = i
  if(mIndex % 2 == 0):
    newState[mIndex] = 0
  else:
    newState[mIndex] = newState[mIndex-1]
    newState[mIndex-1] = 0
  newState[zIndex] = aMove
  if((zIndex % 2 == 0) and (newState[zIndex] > newState[zIndex+1])):
    newState[zIndex] = newState[zIndex+1]
    newState[zIndex+1] = aMove
  return newState    

# a heuristic for how far this state is from being sorted
def stateScore(someState):
  return sum([1 if someState[i] != WINNINGSTATE[i] else 0 for i in range(BUCKETS*2)])    

# go!
while(True):
  triedStates[tuple(state)] = maxDepth
  print('Trying depth', maxDepth)
  if(doSearch(state, maxDepth)):
    print('winning moves are: ', winningMoves)
    break
  maxDepth += 1

Ho valutato questo perché è utile vedere soluzioni ottimali, ma nota che questo non viene eseguito in tempo polinomiale nel numero di bucket per mossa, come richiesto dalla domanda. Non credo che qualsiasi algoritmo che produca una soluzione ottimale (in generale) possa essere eseguito in tempi polinomiali.
Orby,

Per il primo problema di test, il programma genera 10, 8, 1, 12, 6, 7, 11, 3, 5, 13, 4, 9, che non è una soluzione valida. Penso che potresti aver frainteso la domanda. Si noti che la domanda afferma che "Puoi spostare qualsiasi mela da un secchio a un secchio adiacente", ovvero il secchio a destra o a sinistra di esso (non un secchio arbitrario).
Orby,

Oh, ho completamente perso la restrizione di adiacenza! Dopo aver pubblicato questo post, avevo il sospetto fastidioso che anche la limitazione del tempo di esecuzione fosse violata. Non ero sicuro al 100% mentre lo stavo scrivendo, perché l'elemento di programmazione dinamica di evitare stati ripetuti mi ha confuso. Grazie per il voto, anche se questo fallisce per due motivi; questo è un puzzle divertente e vedrò se riesco a trovare una risposta migliore e valida.
RT
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.