Hilf mir, meine Socken zu sortieren!

30

Ich habe einen Stapel sauberer Socken, die ich paarweise sortieren möchte. Leider kann ich nur Socken von beiden Enden des Stapels nehmen, nicht von der Mitte. Außerdem kann ich jeweils nur ein passendes Paar Socken vom Stapel nehmen. Meine Strategie besteht darin, den Stapel zunächst in einen oder mehrere kleinere Stapel aufzuteilen. Ich denke, einige Beispiele werden dies verdeutlichen. Ich werde jede Socke als positive Ganzzahl darstellen (übereinstimmende Ganzzahlen zeigen gleiche Socken an).

Wenn der erste Haufen Socken ist

1 2 3 3 2 1

dann muss ich nicht spalten. Ich kann beide 1Socken ausziehen, dann beide 2Socken, dann beide 3Socken.

Wenn stattdessen der Anfangshaufen ist

1 2 3 2 3 1

dann muss ich es zuerst teilen, weil ich nicht alle Socken koppeln kann, indem ich sie einfach vom Ende entferne. Eine Möglichkeit besteht darin, es in zwei Stapel aufzuteilen

1 2 3 and 2 3 1

Jetzt kann ich die 1Socken ausziehen, gehen 2 3 and 2 3, gefolgt von den 3Socken 2 and 2, gehen und schließlich die 2Socken.

Deine Arbeit

Schreiben Sie ein Programm, das den anfänglichen Sockenstapel in kleinere Stapel aufteilt, damit ich die Socken sortieren kann. Ihr Programm sollte den Stapel in die geringstmögliche Anzahl von Stapeln aufteilen (wenn es mehrere beste Lösungen gibt, müssen Sie nur eine finden).

Die Eingabe wird in Form einer Liste, einer durch Trennzeichen getrennten Zeichenfolge oder einer anderen geeigneten Form angegeben. Es enthält nur Ganzzahlen zwischen 1und einen Maximalwert n, wobei jede Ganzzahl genau zweimal vorkommt.

Die Ausgabe sollte aus der in kleinere Listen aufgeteilten Eingabeliste bestehen, die in einer beliebigen geeigneten Form angegeben wird.

Beispiele

Input             Sample Output
1 1               1 1
1 2 1 2           1; 2 1 2
1 3 2 4 3 2 1 4   1 3 2; 4 3 2 1 4
1 2 3 4 3 4 1 2   1; 2 3; 4 3 4 1 2
1 1 2 2 3 3       1 1 2; 2 3 3
4 3 4 2 2 1 1 3   4 3 4 2; 2 1 1 3

Beachten Sie, dass dies nicht die einzige Ausgabe ist, die für die meisten dieser Eingaben zulässig ist. Für den zweiten Fall würden beispielsweise die Ausgänge 1 2; 1 2oder 1 2 1; 2auch akzeptiert.

Vielen Dank an Sp3000 für einige Testvorschläge!

Ich hasse es, viel Zeit damit zu verbringen, meine Klamotten zu sortieren, also mach deinen Code so kurz wie möglich. Kürzeste Antwort in Bytes gewinnt!

Anmerkungen

  • Ich möchte nicht hinter eine Socke schauen müssen, um zu sehen, ob das passende Paar vorhanden ist, daher ist es nicht erlaubt, beide Socken in einem Paar vom selben Ende zu nehmen. ZB wenn der Stapel 1 1 2 2dann ist, könntest du ihn nicht als einen Stapel lassen und beide 1Socken vom linken Ende nehmen.
Carmeister
quelle
5
Darf ich Sie bei PPCG Carmeister begrüßen? Dies ist eine sehr gute erste Herausforderung +1.
Logic Knight
1
Willkommen bei PPCG! Dies ist eine sehr gute erste Frage. Obwohl diese Frage keine größeren Probleme zu haben scheint, empfehlen wir Benutzern, die Sandbox zu verwenden, um Feedback zu ihren Herausforderungen zu erhalten, bevor Sie sie veröffentlichen.
Mego
Könnte 123213also aufgeteilt werden in 1; 23; 213( 1; 23; 213-> 1; 2; 21-> ; 2; 2)?
R. Kap
@ Mego Danke! Ich werde das in Zukunft sicher tun. @ R.Kap Das wäre ein gültiger Weg, um es zu teilen, aber die Antwort sollte eine Aufteilung ergeben, die es in die kleinstmögliche Anzahl von Stapeln aufteilt. Da es möglich ist, 123213mit nur zwei Stapeln zu teilen , müsste Ihre Antwort einen von zwei Stapeln ergeben.
Carmeister
1
@auch ich bin mir nicht ganz sicher, ob ich deine Frage verstehe, aber die verfügbaren Socken sind die am Anfang jedes Stapels und die am Ende jedes Stapels.
Carmeister

Antworten:

6

Pyth, 25 Bytes

hf!u #-R.-F{BhMs_BMGGT)./

Testsuite

Erläuterung:

hf!u #-R.-F{BhMs_BMGGT)./
                       ./    Form all partitions (implicitly) of the input.
 f                           Filter the permutations on
   u                 T)      Run the following function on the partition
                             until it reaches a fixed point:
                _BMG         Bifurcate the lists on reversal
               s             Concatenate
             hM              Take the first element of each list. 
                             These elements are all the ones on the ends of lists.
           {B                Bifurcate on deduplication
        .-F                  Bagwise subtraction.
                             Only elements repeated in ends of lists remain.
      -R            G        Remove these elements from each list.
   ' #'                      Filter out empty lists.
  !                          Negate. Only an empty list as fixed point succeeds.
h                            Output the first successful partition.
isaacg
quelle
5

JavaScript (ES6), 329

Keine leichte Aufgabe für eine Sprache ohne eingebaute Kombinatorik.

Wahrscheinlich etwas golferischer.

Hinweis: Alle Partitionen haben mindestens die Größe 2, da eine Partition mit einem einzelnen Element immer weniger nützlich ist.

Example: [1] [2 3 4] // can take 1 or 2 or 4  
Better: [1 2] [3 4] // can take 3 too  
a=>{G=(v,i,u=v)=>{if(i--){for(;r[i]=--u;)if(G(u,i))return 1;}else for(w=[...r,n=l].map((x,i)=>a.slice(z,z=x-~i),z=0),y=w.join`;`;w.map(b=>[0,1].map(q=>(x=b[q*=~-b.length])&&(t[x]?([c,p]=t[x],n-=2,p?c.pop():c.shift(),q?b.pop():b.shift()):t[x]=[b,q])),c=0,t=[]),c;)if(!n)return 1};for(l=a.length,r=[],k=0;!G(l-k-1,k);k++);return y}

Erklärung in Teilen

(Es ist zu ausführlich, aber ich fand es schwierig zu erklären - überspringen Sie schließlich, um "alles zusammen zu setzen")

Eine rekursive Funktion zum Auflisten aller möglichen Teilungen eines Arrays

// v: array length
// i number of splits
// fill the global array r that must exists
G=(v,i,u=v)=>
{
  if(i--)
  {
    for(;r[i]=--u;)
      G(u,i)
  }
  else
  {
    // the current split position are in r, ready to use
    // for instance...
    parts = [...r,a.length].map(x=>a.slice(z,z=x),z=0)
    console.log(r, parts)
  }
};

r=[]
a=['A','B','C','D']
G(4, 2)

// output in console (firebug)
[2, 3] [["A", "B"], ["C"], ["D"]]
[1, 3] [["A"], ["B", "C"], ["D"]]
[1, 2] [["A"], ["B"], ["C", "D"]]

Jetzt benötige ich Partitionen der Größe 2 oder mehr, daher muss ich diese Funktion mit leicht unterschiedlichen Parametern verwenden. Der Parameter v ist "Arraygröße - Anzahl der gewünschten Partitionen - 1". Dann muss ich die Partitionen etwas anders aufbauen.

// Same call (4,2), same r, but the array b is of size 7
part = [...r,b.length].map((x,i)=>
          b.slice(z,z=x+i+1) // add 1 more element to each partition
       ,z=0))
// output in console (firebug) 
[2, 3] [["A", "B", "C"], ["D", "E"], ["F", "G"]]
[1, 3] [["A", "B"], ["C", "D", "E"], ["F", "G"]]
[1, 2] [["A", "B"], ["C", "D"], ["E", "F", "G"]]

So kann ich die Liste der Partitionen für keine Teilung, 1 Teilung, 2 Teilungen usw. aufzählen. Wenn ich eine funktionierende Partition finde, halte ich an und gebe das gefundene Ergebnis aus.

Um dies zu überprüfen, scannen Sie die Partitionsliste, notieren Sie sich die Werte am Anfang und am Ende jeder Partition. Wenn Sie einen repetierten Wert gefunden haben, entfernen Sie ihn. Wiederholen, bis endlich nichts mehr entfernt werden kann: Wenn alle Paare entfernt wurden, ist diese Partition in Ordnung.

t = []; // array to note the repeated values
// t[x] == [
//           subarray holding value x, 
//           position of value x (I care zero or nonzero)
//         ]
n = a.length // counter start, must reach 0
// remember part just in case, because this check will destroy it 
result = part.join(';') // a string representation for return value
do
{
  // in the golfed code there is a forr loop
  // all this body is inside the for condition
  c = 0; // init c to a falsy, if a pair is found c becomes truthy
  part.forEach(b=> // b: array, current partition
    [0,1].forEach(q=> ( // exec for 0 (start), 1 (end)
      q *= b.length-1, // now q is the correct index
      x = b[q]) // x is the value at start or end
      x && ( // b could be empty, check that x is not 'undefined'
        t[x] ? // is there a value in t at position x?
           ( // yes, remove the pair
             n-=2, // pair found, decrement counter
             [c, p] = t[x], // get stored array and position
             p ? c.pop() : c.shift(), // remove from c at start or end
             q ? b.pop() : b.shift()  // remove twin value from b
           )
           : // no, remember the value in t
             t[x] = [b, q]
    )) // end [0,1].forEach
  ) // end part.forEach
}
while (c) // repeat until nothing can be removed
if(!n) return 1 // wow, result found (in 'result' variable)

Dann ist der fehlende Teil nur eine Schleife, die die G-Funktion zur Erhöhung der Partitionsanzahl abruft. Die Schleife wird beendet, wenn ein Ergebnis gefunden wird.

Alles zusammen

F=a=>{
  G=(v,i,u=v)=>{
    if (i--)
    {
      for(; r[i]=--u; )
        if (G(u,i)) 
          return 1;
    }
    else
    {
      w = [...r,n=l].map((x,i)=>a.slice(z, z = x-~i), z = 0);
      y = w.join`;`;
      for(; // almost all the for body is inside the condition
        w.map(b=>
          [0,1].map(q=>
            (x=b[q*=~-b.length])
             &&(t[x]
                ?([c,p]=t[x],n-=2,
                   p?c.pop():c.shift(),
                   q?b.pop():b.shift())
                :t[x]=[b,q])) // end [0,1].map
          ,c=0,t=[] // init variables for w.map
        ),c; // the loop condition is on c
      )
        if(!n)return 1 // this is the for body
    }
  };
  for(l = a.length, r = [], k = 0; !G(l-k-1, k); k++);
  return y
}

Prüfung

F=a=>{G=(v,i,u=v)=>{if(i--){for(;r[i]=--u;)if(G(u,i))return 1;}else for(w=[...r,n=l].map((x,i)=>a.slice(z,z=x-~i),z=0),y=w.join`;`;w.map(b=>[0,1].map(q=>(x=b[q*=~-b.length])&&(t[x]?([c,p]=t[x],n-=2,p?c.pop():c.shift(),q?b.pop():b.shift()):t[x]=[b,q])),c=0,t=[]),c;)if(!n)return 1};for(l=a.length,r=[],k=0;!G(l-k-1,k);k++);return y}

console.log=x=>O.textContent+=x+'\n'

TestData=[[1,1],[1,2,1,2],[1,3,2,4,3,2,1,4],[1,2,3,4,3,4,1,2],[1,1,2,2,3,3],[4,3,4,2,2,1,1,3]]

TestData.forEach(t=>console.log(t+' -> '+F(t)))

function RandomTest() {
  var l=I.value*2
  var a=[...Array(l)].map((_,i)=>1+i/2|0)
  a.map((v,i)=>a[a[i]=a[j=0|i+Math.random()*(a.length-i)],j]=v) // shuffle
  Q.textContent=a+''+'\n\n'+F(a).replace(/;/g, ';\n') // better readability
}
Base test
<pre id=O></pre>
Random test. Number of pairs: <input id=I value=15><button onclick="RandomTest()">-></button>
<pre id=Q></pre>

edc65
quelle