Sortieren Sie einige Äpfel!

11

Problem

Stellen Sie sich 7 Eimer hintereinander vor. Jeder Eimer kann höchstens 2 Äpfel enthalten. Es gibt 13 Äpfel mit den Bezeichnungen 1 bis 13. Sie sind auf die 7 Eimer verteilt. Beispielsweise,

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

Wobei 0 den leeren Raum darstellt. Die Reihenfolge, in der die Äpfel in jedem Eimer erscheinen, ist nicht relevant (z. B. entspricht {5,4} {4,5}).

Sie können jeden Apfel von einem Eimer in einen benachbarten Eimer verschieben, sofern im Ziel-Eimer Platz für einen anderen Apfel ist. Jede Bewegung wird durch die Nummer des Apfels beschrieben, den Sie bewegen möchten (was eindeutig ist, da nur ein Leerzeichen vorhanden ist). Zum Beispiel den Zug anwenden

7

zu der obigen Anordnung würde führen

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

Zielsetzung

Schreiben Sie ein Programm, das eine Anordnung aus STDIN liest und in die folgende Anordnung sortiert

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

mit so wenig Zügen wie möglich. Auch hier ist die Reihenfolge, in der die Äpfel in jedem Eimer erscheinen, nicht relevant. Die Reihenfolge der Eimer spielt eine Rolle. Es sollte die Bewegungen ausgeben, die zum Sortieren jeder durch Kommas getrennten Anordnung verwendet werden. Beispielsweise,

13, 7, 6, ...

Ihre Punktzahl entspricht der Summe der Anzahl der Züge, die zur Lösung der folgenden Vorkehrungen erforderlich sind:

{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}

Ja, jede dieser Vereinbarungen hat eine Lösung.

Regeln

  • Ihre Lösung muss in Polynomzeit in der Anzahl der Buckets pro Zug ausgeführt werden. Es geht darum, clevere Heuristiken zu verwenden.
  • Alle Algorithmen müssen deterministisch sein.
  • Bei einem Gleichstand gewinnt die kürzeste Byteanzahl.
Oder von
quelle
2
Was bringt es, das Ziel zu kennzeichnen, wenn es nur ein Feld gibt, in das Sie einen Apfel verschieben können?
John Dvorak
Was ist, wenn meine Brute-Force-Lösung in angemessener Zeit ausgeführt wird? Es gibt nur 700 Millionen Zustände - in wenigen Minuten leicht aufzählbar. Definieren Sie "angemessene Zeit".
John Dvorak
@ JanDvorak Per "Was ist der Sinn" - guter Anruf. Das war mir nicht eingefallen. Ich definiere hier vernünftig als weniger als die Zeit, die erforderlich ist, um die Lösung
brutal
Bedeutet Ihre Definition von "vernünftig", dass wir zuerst die Brute-Force-Lösung implementieren sollten, dann zählt alles, was schneller ist?
John Dvorak
Ist die endgültige Bestellung des Eimers wichtig?
AMK

Antworten:

4

Punktzahl: 448

Meine Idee ist es, sie fortlaufend zu sortieren, beginnend mit 1. Dies gibt uns die schöne Eigenschaft, dass wir genau wissen, welche der beiden Äpfel wir bewegen müssen, wenn wir den Platz in den vorherigen / nächsten Korb verschieben möchten - die max / min eins. Hier ist die Testaufschlüsselung:

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

Total score: 448 moves

Der Code kann viel mehr gespielt werden, aber eine bessere Qualität des Codes führt zu zusätzlichen Antworten.

C ++ (501 Bytes)

#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);}}
        }
    }
}

Weitere Verbesserungen können darin bestehen, zu C zu wechseln und zu versuchen, die Punktzahl zu senken, indem von den großen Werten abwärts ausgegangen wird (und schließlich beide Lösungen kombiniert werden).

yasen
quelle
1
Eine Teilzeichenfolge Ihres Codes bildet bereits ein C-Programm. Insbesondere könnte es in C funktionieren, indem einfach die erste Zeile gelöscht wird.
Feersum
@feersum Du hast recht. Am Anfang hatte ich mehr C ++ - spezifischen Code, aber danach, mit dem Wechsel zu C, schien ich ihn loszuwerden.
Yasen
Können Sie angeben, dass Sie das Eingabeformat in Ihrer Lösung geändert haben, um es denjenigen klarer zu machen, die versuchen, es zu überprüfen?
Orby
2

C 426 448

Dies sortiert Äpfel nacheinander von 1 bis 13, ähnlich wie bei Yasen , es sei denn, es besteht die Möglichkeit, eine größere Zahl nach oben oder eine kleinere Zahl nach unten zu verschieben. Leider verbessert dies nur die Leistung beim ersten Testproblem, aber es ist eine kleine Verbesserung. Ich habe beim Ausführen der Testprobleme einen Fehler gemacht. Es scheint, als hätte ich Yasens Methode einfach neu implementiert.

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

Es werden Eingaben ohne Klammern oder Kommas vorgenommen, z

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

Hier ist der Golfcode, der bei 423 Bytes eingeht und ein paar unnötige Zeilenumbrüche zählt (könnte wahrscheinlich mehr Golf spielen, aber ich erwarte, dass diese Punktzahl übertroffen wird):

#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;}}

Und der Code ohne Wolf, der auch die Partitur druckt:

#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);
}
Oder von
quelle
2

Python 3 - 121

Dies implementiert eine Tiefensuche mit zunehmender Tiefe, bis eine Lösung gefunden wird. Es verwendet ein Wörterbuch, um besuchte Zustände zu speichern, so dass es sie nur mit einem Fenster mit höherer Tiefe wieder besucht. Bei der Entscheidung, welche Zustände überprüft werden sollen, wird die Anzahl der falsch platzierten Elemente als Heuristik verwendet und nur die bestmöglichen Zustände aufgerufen. Da die Reihenfolge der Elemente in ihrem Bucket keine Rolle spielt, wird immer eine Reihenfolge innerhalb der Buckets beibehalten. Dies erleichtert die Überprüfung, ob ein Element falsch platziert ist.

Die Eingabe ist ein Array von Ints, wobei das erste Int die Anzahl der Buckets ist.

Zum Beispiel für # 8 (dieser dauert sehr lange, bis er auf meinem Computer ausgeführt wird, die anderen sind in Sekunden fertig):

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

Hier sind die Ergebnisse des Testsatzes: # 1: 12, # 2: 12, # 3: 12, # 4: 12, # 5: 11, # 6: 11, # 7: 10, # 8: 14, # 9: 13, # 10: 14

Hier ist der Code:

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
RT
quelle
Ich habe dies positiv bewertet, da es nützlich ist, optimale Lösungen zu finden, aber beachten Sie, dass dies nicht in Polynomzeit in der Anzahl der Buckets pro Zug ausgeführt wird, wie in der Frage gefordert. Ich glaube nicht, dass ein Algorithmus, der (im Allgemeinen) eine optimale Lösung liefert, in Polynomzeit ausgeführt werden kann.
Orby
Für das erste Testproblem generiert Ihr Programm 10, 8, 1, 12, 6, 7, 11, 3, 5, 13, 4, 9, was keine gültige Lösung ist. Ich denke, Sie haben die Frage möglicherweise falsch verstanden. Beachten Sie, dass die Frage lautet: "Sie können jeden Apfel von einem Eimer in einen benachbarten Eimer verschieben", dh den Eimer rechts oder links davon (kein beliebiger Eimer).
Orby
Oh, ich habe die Adjazenzbeschränkung total verpasst! Nachdem ich dies gepostet hatte, hatte ich den nagenden Verdacht, dass auch die Laufzeitbeschränkung verletzt wurde. Ich war mir beim Schreiben nicht 100% sicher, weil mich das dynamische Programmierelement zur Vermeidung wiederholter Zustände verwirrte. Vielen Dank für die positive Abstimmung, auch wenn dies in zweierlei Hinsicht fehlschlägt. Dies ist ein lustiges Rätsel und ich werde sehen, ob ich eine bessere, gültige Antwort finden kann.
RT