Aidez-moi à trier mes chaussettes!

30

J'ai un tas de chaussettes propres que je veux trier par paires. Malheureusement, je ne peux prendre que des chaussettes aux deux extrémités de la pile, pas au milieu. De plus, je ne peux retirer des chaussettes de la pile qu'une paire assortie à la fois. Ma stratégie consiste à diviser d'abord la pile en une ou plusieurs piles plus petites. Je pense que certains exemples le montreront clairement. Je vais représenter chaque chaussette comme un entier positif (les entiers correspondants indiquent des chaussettes égales).

Si le premier tas de chaussettes est

1 2 3 3 2 1

alors je n'ai pas à faire de fractionnement. Je peux retirer les deux 1chaussettes, puis les deux 2chaussettes, puis les deux 3chaussettes.

Si à la place la pile initiale est

1 2 3 2 3 1

ensuite je dois le diviser d'abord parce que je ne pourrai pas associer toutes les chaussettes en les retirant simplement de la fin. Une possibilité est de le diviser en deux piles

1 2 3 and 2 3 1

Maintenant, je peux retirer les 1chaussettes en partant 2 3 and 2 3, puis les 3chaussettes en partant 2 and 2et enfin les 2chaussettes.

Votre travail

Compte tenu de la pile initiale de chaussettes, écrivez un programme qui la divisera en piles plus petites qui me permettront de trier les chaussettes. Votre programme doit diviser la pile en le moins de piles possible (s'il existe plusieurs meilleures solutions, il vous suffit d'en trouver une).

L'entrée sera donnée sous forme de liste, chaîne délimitée ou autre forme pratique. Il ne contiendra que des entiers compris entre 1une valeur maximale net chaque entier se produisant exactement deux fois.

La sortie doit consister en la liste d'entrée divisée en listes plus petites, données sous n'importe quelle forme pratique.

Exemples

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

Notez que ce n'est pas la seule sortie autorisée pour la plupart de ces entrées. Pour le deuxième cas, par exemple, les sorties 1 2; 1 2ou 1 2 1; 2seraient également acceptées.

Merci à Sp3000 pour quelques suggestions de tests!

Je déteste passer beaucoup de temps à trier mes vêtements, alors faites votre code aussi court que possible. La réponse la plus courte en octets gagne!

Remarques

  • Je ne veux pas avoir à regarder derrière une chaussette pour voir si sa paire assortie est là, donc prendre les deux chaussettes dans une paire du même bout n'est pas autorisé. Par exemple, si la pile est 1 1 2 2alors vous ne pourriez pas la laisser comme une seule pile et prendre les deux 1chaussettes de l'extrémité gauche.
Carmeister
la source
5
Puis-je souhaiter la bienvenue à PPCG Carmeister. C'est un très bon premier défi +1.
Logic Knight
1
Bienvenue chez PPCG! C'est une très bonne première question. Bien que cette question ne semble pas poser de problème majeur, nous encourageons les utilisateurs à utiliser le Sandbox pour recevoir des commentaires sur leurs défis avant de les publier.
Mego
Donc, 123213pourrait être divisé en 1; 23; 213( 1; 23; 213-> 1; 2; 21-> ; 2; 2)?
R. Kap
@Mego Merci! Je serai sûr de le faire à l'avenir. @ R.Kap Ce serait un moyen valide de le diviser, mais la réponse devrait donner un fractionnement qui le divise en le plus petit nombre possible de piles. Puisqu'il est possible de diviser en 123213utilisant seulement deux piles, votre réponse devrait donner l'une des divisions en deux piles.
Carmeister
1
@ven Je ne suis pas tout à fait sûr de comprendre votre question, mais les chaussettes disponibles sont celles au début de chaque pile et celles à la fin de chaque pile.
Carmeister

Réponses:

6

Pyth, 25 octets

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

Suite de tests

Explication:

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
la source
5

JavaScript (ES6), 329

Ce n'est pas une tâche facile pour un langage qui n'a pas de fonctions combinatoires intégrées.

Probablement un peu plus golfable.

Remarque: toutes les partitions sont au moins de taille 2, car une partition avec un seul élément est toujours moins 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}

Explication en parties

(c'est trop verbeux, mais j'ai trouvé difficile à expliquer - finissez par passer à "tout mettre ensemble")

Une fonction récursive pour énumérer toutes les divisions possibles d'un tableau

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

Maintenant, j'ai besoin de partitions de taille 2 ou plus, donc je dois utiliser cette fonction avec des paramètres légèrement différents. Le paramètre v est "taille du tableau - nombre de partitions souhaitées - 1". Ensuite, je dois construire les partitions d'une manière légèrement différente.

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

Donc, je peux énumérer la liste des partitions pour aucune division, 1 division, 2 divisions et ainsi de suite. Lorsque je trouve une partition qui fonctionne, je m'arrête et je renvoie le résultat trouvé.

Pour vérifier, scannez la liste des partitions, notez les valeurs au début et à la fin de chacune, si trouvé une valeur répétée puis supprimez-la. Répétez jusqu'à ce que rien ne puisse être supprimé, enfin: si toutes les paires ont été supprimées, cette partition est bonne.

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)

Ensuite, la partie manquante n'est qu'une boucle appelant la fonction G augmentant le nombre de partitions. La boucle se termine lorsqu'un résultat est trouvé.

Mets le tout ensemble

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
}

Tester

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
la source