Aiutami a ordinare i miei calzini!


30

Ho un mucchio di calzini puliti che voglio ordinare in coppia. Sfortunatamente, posso prendere solo i calzini da entrambe le estremità della pila, non dal centro. Inoltre, posso solo rimuovere un paio di calzini dalla pila alla volta. La mia strategia è prima di dividere il mucchio in una o più pile più piccole. Penso che alcuni esempi lo chiariranno. Rappresenterò ogni calzino come intero positivo (numeri interi corrispondenti indicano calzini uguali).

Se la pila iniziale di calze è

1 2 3 3 2 1

quindi non devo fare alcuna scissione. Posso rimuovere entrambe le 1calze, quindi entrambe le 2calze, quindi entrambe le 3calze.

Se invece la pila iniziale è

1 2 3 2 3 1

quindi devo prima dividerlo perché non sarò in grado di accoppiare tutti i calzini semplicemente rimuovendoli dall'estremità. Una possibilità è di dividerlo in due pile

1 2 3 and 2 3 1

Ora posso rimuovere i 1calzini, lasciando 2 3 and 2 3, seguiti dai 3calzini, lasciando 2 and 2, e infine i 2calzini.

Il tuo lavoro

Data la pila iniziale di calze, scrivi un programma che lo dividerà in pile più piccole che mi permetteranno di ordinare le calze. Il tuo programma dovrebbe dividere il mucchio nel minor numero possibile di pile (se ci sono più soluzioni migliori, devi solo trovarne una).

L'input verrà fornito come elenco, stringa delimitata o altra forma conveniente. Conterrà solo numeri interi tra 1e un valore massimo n, con ogni numero intero che si verifica esattamente due volte.

L'output dovrebbe essere costituito dall'elenco di input suddiviso in elenchi più piccoli, fornito in qualsiasi forma conveniente.

Esempi

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

Si noti che questo non è l'unico output consentito per la maggior parte di questi input. Per il secondo caso, ad esempio, le uscite 1 2; 1 2o 1 2 1; 2sarebbero anche accettate.

Grazie a Sp3000 per alcuni suggerimenti di prova!

Odio passare molto tempo a sistemare i miei vestiti, quindi abbassa il codice il più possibile. Vince la risposta più breve in byte!

Gli appunti

  • Non voglio guardare dietro un calzino per vedere se la sua coppia abbinata è lì, quindi non è permesso prendere entrambi i calzini in un paio dalla stessa estremità. Ad esempio, se la pila è 1 1 2 2allora non saresti in grado di lasciarla come una pila e prendere entrambe le 1calze dall'estremità sinistra.

5
Posso dire di essere il benvenuto in PPCG Carmeister. Questa è un'ottima prima sfida +1.
Logic Knight,

1
Benvenuti in PPCG! Questa è un'ottima prima domanda. Sebbene questa domanda non sembri avere grossi problemi, incoraggiamo gli utenti a utilizzare Sandbox per ricevere feedback sulle loro sfide prima di pubblicarle.
Mego

Quindi 123213potrebbe essere suddiviso in 1; 23; 213( 1; 23; 213-> 1; 2; 21-> ; 2; 2)?
R. Kap,

@Mego Grazie! Sarò sicuro di farlo in futuro. @ R.Kap Sarebbe un modo valido per dividerlo, ma la risposta dovrebbe dare una scissione che lo divide nel minor numero possibile di pile. Dal momento che è possibile dividere 123213usando solo due pile, la tua risposta dovrebbe dare una delle divisioni a due pile.
Carmeister,

1
@ven Non sono del tutto sicuro di aver capito la tua domanda, ma i calzini disponibili sono quelli all'inizio di ogni pila e quelli alla fine di ogni pila.
Carmeister,

Risposte:


6

Pyth, 25 byte

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

Suite di test

Spiegazione:

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.

5

JavaScript (ES6), 329

Non è un compito facile per un linguaggio che non ha builtin combinatori.

Probabilmente leggermente più golfabile.

Nota: tutte le partizioni hanno almeno la dimensione 2, poiché una partizione con un singolo elemento è sempre meno utile.

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}

Spiegazione in parti

(È eccessivamente prolisso, ma ho trovato difficile da spiegare - alla fine saltare a "mettere tutto insieme")

Una funzione ricorsiva per enumerare tutte le possibili divisioni di un array

// 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"]]

Ora ho bisogno di partizioni di dimensione 2 o superiore, quindi devo usare questa funzione con parametri leggermente diversi. Il parametro v è "dimensione array - numero di partizioni desiderate - 1". Quindi devo costruire le partizioni in un modo leggermente diverso.

// 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"]]

Quindi, posso elencare l'elenco delle partizioni per nessuna divisione, 1 divisione, 2 divisione e così via. Quando trovo una partizione funzionante mi fermerò e produrrò il risultato trovato.

Per verificare, scansionare l'elenco delle partizioni, annotare i valori all'inizio e alla fine di ciascuno, se viene trovato un valore replicato, quindi rimuoverlo. Ripeti fino a quando non sarà più possibile rimuovere nulla: se tutte le coppie sono state rimosse, questa partizione è buona.

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)

Quindi, la parte mancante è solo un loop che chiama la funzione G aumentando il conteggio delle partizioni. L'uscita del loop quando viene trovato un risultato.

Metterli tutti insieme

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
}

Test

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>

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.