Помоги мне разобрать мои носки!

30

У меня есть куча чистых носков, которые я хочу разбить на пары. К сожалению, я могу брать только носки с любого конца кучи, а не с середины. Кроме того, я могу удалить носки из кучи только соответствующей пары одновременно. Моя стратегия состоит в том, чтобы сначала разбить кучу на одну или несколько меньших куч. Я думаю, что некоторые примеры прояснят это. Я буду представлять каждый носок как положительное целое число (совпадающие целые числа обозначают равные носки).

Если начальная куча носков

1 2 3 3 2 1

тогда мне не нужно делать никакого разделения. Я могу снять оба 1носка, затем оба 2носка, затем оба 3носка.

Если вместо этого начальная куча

1 2 3 2 3 1

затем я должен сначала разделить его, потому что я не смогу соединить все носки, просто сняв их с конца. Одна возможность состоит в том, чтобы разделить это на две груды

1 2 3 and 2 3 1

Теперь я могу снять 1носки, уйти 2 3 and 2 3, затем 3носки, уйти 2 and 2и, наконец, 2носки.

Твоя работа

Учитывая начальную кучу носков, напишите программу, которая разделит ее на более мелкие, что позволит мне сортировать носки. Ваша программа должна разбить кучу на наименьшее количество возможных куч (если есть несколько лучших решений, вам нужно найти только одну).

Входные данные будут представлены в виде списка, строки с разделителями или другой удобной формы. Он будет содержать только целые числа между 1и некоторым максимальным значением n, причем каждое целое число встречается ровно дважды.

Вывод должен состоять из входного списка, разбитого на меньшие списки, представленные в любой удобной форме.

Примеры

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

Обратите внимание, что это не единственный допустимый выход для большинства из этих входов. Для второго случая, например, выходы 1 2; 1 2или 1 2 1; 2также будут приняты.

Спасибо Sp3000 за некоторые предложения по тестированию!

Я ненавижу тратить много времени на сортировку моей одежды, поэтому сделайте ваш код максимально коротким. Кратчайший ответ в байтах побеждает!

Заметки

  • Я не хочу смотреть за носком, чтобы увидеть, есть ли у него соответствующая пара, поэтому брать оба носка в паре с одного конца нельзя. Например, если куча есть, 1 1 2 2то вы не сможете оставить ее как одну кучу и взять оба 1носка с левого конца.
Carmeister
источник
5
Могу ли я сказать, добро пожаловать в PPCG Carmeister. Это очень хороший первый вызов +1.
Рыцарь логики
1
Добро пожаловать в PPCG! Это очень хороший первый вопрос. Хотя этот вопрос, похоже, не имеет каких-либо серьезных проблем, мы рекомендуем пользователям использовать Песочницу, чтобы получать отзывы о своих проблемах до их публикации.
Mego
Так 123213можно разделить на 1; 23; 213( 1; 23; 213-> 1; 2; 21-> ; 2; 2)?
Р. Кап
@ Мего Спасибо! Я обязательно сделаю это в будущем. @ R.Kap Это был бы верный способ разбить его, но ответ должен дать разбивку, которая разбивает его на наименьшее возможное количество стопок. Поскольку можно разделить, 123213используя только две стопки, ваш ответ должен будет дать одну из двухсоставных.
Carmeister
1
@ven Я не совсем уверен, что понимаю ваш вопрос, но носки можно взять в начале каждой стопки и в конце каждой стопки.
Carmeister

Ответы:

6

Pyth, 25 байт

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

Тестирование

Объяснение:

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
источник
5

JavaScript (ES6), 329

Непростая задача для языка, в котором нет встроенных комбинаторных функций.

Вероятно, чуть более пригодный для игры в гольф.

Примечание: все разделы имеют размер не менее 2, так как раздел с одним элементом всегда менее полезен.

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}

Пояснение по частям

(это слишком многословно, но мне было сложно объяснить - в конце концов пропустить, чтобы «собрать все вместе»)

Рекурсивная функция для перечисления всех возможных разбиений массива

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

Теперь мне нужны разделы размером 2 или больше, поэтому я должен использовать эту функцию со слегка различными параметрами. Параметр v равен «размер массива - количество желаемых разделов - 1». Тогда я должен построить разделы немного по-другому.

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

Итак, я могу перечислить список разделов без разделения, 1 разделения, 2 разделения и так далее. Когда я найду рабочий раздел, я остановлюсь и выведу найденный результат.

Чтобы проверить, просканируйте список разделов, запишите значения в начале и в конце каждого из них, если найдено повторяющееся значение, затем удалите его. Повторяйте до тех пор, пока ничего не будет удалено, наконец: если все пары были удалены, этот раздел хорош.

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)

Затем недостающая часть - это просто цикл, вызывающий функцию G, увеличивающую количество разделов. Выход из цикла, когда результат найден.

Поставить все это вместе

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
}

Тест

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
источник