Вибіркове вбивство натуральних чисел


21

Вступ

Арифметичний гаол - це спеціальний інструмент, який містить в собі додатні цілі числа. Однак останнім часом натуральні числа намагаються втекти. Тому підопічні вирішили усунути деякі додатні цілі числа, щоб надіслати повідомлення іншим цілим числам. Вони найняли інженера-програміста, щоб написати програму, щоб визначити, які цілі числа слід усунути для досягнення максимального ефекту.

Опис вводу

Введення даних здійснюється через STDIN, аргументи командного рядка або користувальницьку функцію введення (наприклад, raw_input). Ви не можете мати його як аргумент функції або преініціалізовану змінну (наприклад, ця програма очікує введення змінної x).

Перший рядок введення містить єдине додатне ціле число nде 8 >= n >= 3. Далі йдуть nрядки, що містять nсимволи з набору [1,2,3,4,5,6,7,8,9]. Ось приклад введення:

5
22332
46351
65455
24463
65652

Опис виходу

Охоронці хотіли б ліквідувати кількість, щоб виконати такі умови:

  • У кожному рядку та стовпці отриманої сітки жодне число не з’явиться двічі;
  • Жодне два усунених числа не може бути суміжним по горизонталі чи вертикалі;
  • Отримані числа повинні утворювати ортогонально суміжну групу - можна буде переходити від будь-якого збереженого числа до будь-якого іншого вижилого числа, що рухається лише горизонтально та вертикально, і ніколи не перетинати жодне усунене число.

Виведіть вхід (мінус перший рядок), при цьому усунені числа замінюються на #.

Рішення може бути більше. Якщо це так, ви можете вивести будь-яке рішення.

Рішення також може бути. Якщо це так, виведіть рядок no answer.

Ось можливий вихід для прикладу введення:

#2#3#
46351
6#4#5
24#63
#56#2

Приклад Входи та Виходи

Для кожного входу є декілька виходів, тому ці результати є лише прикладами.

Вхід:

5
46551
51565
32654
14423
43244

Вихід:

46#51
#156#
326#4
1#423
#324#

Вхід:

7
7183625
1681563
5238564
8786268
1545382
3814756
5325345

Вихід:

71#362#
#6815#3
5238#64
#7#62#8
154#382
3814756
#325#4#

Вхід:

8
21534768
75196287
68392184
96244853
44865912
76516647
89751326
43698979

Вихід:

21#34768
#5196287
683#21#4
9#24#853
#4865912
7#51#64#
89751326
436#8#7#

Вхід:

4
2222
2331
3112
1322

Вихід:

no answer

4
(Також відомий як сингли .)
Дорночка

Ця головоломка відмінна. Дякую. Робота над рішенням
Не те, що Чарльз

1
Мені подобається ця головоломка, але на неї не можна відповісти "як є", використовуючи JavaScript у браузері, оскільки єдиний спосіб введення користувача promptне дозволяє вводити багато рядків.
edc65

Відповіді:


0

Ruby, 346 344 329 316 байт, sl∞∞∞∞∞∞w

Це кодовий гольф, а не швидкий код, тому ...

N=/!/=~G=$*[1..-1]*?!
M=N*N
g=->i{(!G[i]||G[i]<?*||i<0||A[i])&&break
A[i]=0
[1,N+1,-1,-1-N].map{|a|g[i+a]}}
f=->w,a{A=[];g[/\d/=~G=a];/#.{#{N}}?#/!~G&&/(\d)([^!]*|.{#{N}}.{#{O=N+1}}*)\1/!~G&&A.count(0)+w==M&&N.times.map{|m|puts G[m*(1+N),N]}&&exit
(w...M).map{|j|b=a+'';b[j]=?#
w<M&&f[w+1,b]}}
f[0,G]
$><<"no answer"

Використовуйте його так:

mad_gaksha@madlab ~/Applications/Tools $ ruby -W0 c50442.rb 3 323 312 231
#23
312
231

Прапор -W0не потрібен, але я пропоную вам або додати його, щоб відключити попередження, або перенаправити stderr...

Скажіть, чи вистачало терпіння, щоб запустити це на прикладах для n = 6,7,8

Журнал змін

  • eachmap, завдяки @NotThatCharles
  • перевірити наявність суміжних видалень та однакових цифр двома regexps, подібно до того, що запропонував @NotThatCharles
  • трохи оптимізовано введення читання
  • менше і повільніше

кілька додаткових поліпшень розміру: \dкоротше, ніж [^#]у передостанньої лінії; M.times.to_aдовше, ніж(0..M-1).to_a
Не те, що Чарльз

@NotThatCharles Дякую за поради! Але M.times.to_aце 1 байт коротше , ніж (0..M-1).to_a;) (0...M).to_aтеж працює і має ту ж довжину.
blutorange

... підрахунок важкий
Не те, що Чарльз

чи справді вона завершена, коли n = 8?
Не те, що Чарльз

@NotthatCharles Якщо ви досить довго чекаєте чи використовуєте надшвидкий ПК, так, так і слід. Це кодовий гольф без будь-яких обмежень щодо швидкості, тому я визначив пріоритет довжини коду ...
blutorange

5

Рубі - 541 ..., 394

Основний алгоритм - це рекурсивний пошук глибоко-перших дублікатів для підтвердження вибору, переглядаючи рядок 1, потім стовпець 1, потім рядок 2 тощо і перевіряючи, чи не загинули два сусіди та чи підключена сітка (це break ifпункт у там, і шматочок, який приходить перед цим).

K=(0...(N=gets.to_i)*N).to_a
J=gets(p).split*''
H=->m{K&[m+1,m-1,m+N,m-N]}
Q=->k{s=[k[j=0]]
(j=s.size
s.map{|x|(s+=H[x]&k).uniq!})while s[j]
break if(K-k).any?{|m|(H[m]-k)[0]}||k!=k&s
$><<K.map{|m|[k.index(m)?J[m]:?#,m%N>N-2?"
":p]}*''|exit if !g=((0...N*2).map{|x|(k.select{|m|m.divmod(N)[x/N]==x%N}.group_by{|m|J[m]}.find{|l,c|c[1]}||[])[1]}-[p]).min
g.map{|d|Q[k-g<<d]}}
Q[K]
puts"no answer"

Деякі акуратні хитрощі:

if w[1]набагато коротше, ніж, if !w.one?і якщо ви знаєте, що принаймні один член, це такий же результат.

Так само [0]коротше, ніж any?якщо немає блоку, і s[j]це милий ярлик j<s.size(технічно це більше схоже j.abs<s.size)

І y%N+(y/N).iнабагато коротше, ніжComplex(y%N,y/N)

Крім того, коли для створення рядків є два складних умови, це може бути коротше, [cond1?str1a:str1b,cond2?str2a:str2b]*''ніж додати всі паролі або #{}s.

Розколювання та пояснення:

(Це з версії 531 байт. Я вніс зміни. Найбільш помітно, я з того часу усунув виклик до продукту - просто вирішуйте одну цифру на рядок / стовпець за один раз, і J тепер просто масив, індексований цілі числа. Усі координати - це цілі числа.)

H обчислює сусідів

def H m 
  # m, like all indices, is a complex number 
  #    where the real part is x and the imaginary is y
  # so neighbors are just +/-i and +/-1

  i='i'.to_c
  neighborhood = [m+1, m-1, m+i, m-i]

  # and let's just make sure to eliminate out-of-bounds cells
  K & neighborhood
end

N - розмір сітки

N = gets.to_i

K - це ключі до карти (складні номери)

# pretty self-explanatory
# a range of, e.g., if N=3, (0..8)
# mapped to (0+0i),(1+0i),(2+0i),(0+1i),(1+1i),(2+1i),...
K = (0..N**2-1).map{|y| (y%N) +(y/N).i }

J карта вводу (в'язниця)

# so J is [[0+0,"2"],[0+1i,"3"],....].to_h
J=K.zip($<.flat_map {|s|
  # take each input line, and...
  # remove the "\n" and then turn it into an array of chars 
  s.chomp.chars
}).to_h

k - це ключі, що не вбиваються

# starts as K

Q є основним рекурсивним методом

def Q k
  j=0 # j is the size of mass
  # the connected mass starts arbitrarily wherever k starts
  mass=[k[0]]
  while j < s.size # while s hasn't grown
    j = mass.size   
    mass.each{|cell|
      # add all neighbors that are in k
      (mass+=H[cell] & k).uniq!
    }
  end
  # if mass != k, it's not all orthogonally connected
  is_all_connected = k!=k&mass

  # (K-k) are the killed cells 
  two_neighbors_killed = (K-k).any?{|m|
    # if any neighbors of killed cells aren't in k,
    # it means it was killed, too 
    (H[m]-k)[0]
  }
  # fail fast
  return if two_neighbors_killed || is_all_connected

  def u x
    x.group_by{|m|J[m]}.select{|l,c|c[1]}
  end

  rows_with_dupes = Array.new(N){|r|u[k.select{|m|m.imag==r}]}

  cols_with_dupes = Array.new(N){|r|u[k.select{|m|m.real==r}]}

  # dupes is an array of hashes
  # each hash represents one row or column.  E.g.,
  #   {
  #     "3"=>[(0+0i),(1+0i),(3+0i)],
  #     "2"=>[(2+0i),(4+0i)]
  #   }
  # means that the 0th, 1st and 3rd cells in row 0
  # all are "3", and 2nd and 4th are "2".
  # Any digits without a duplicate are rejected.
  # Any row/col without any dupes is removed here.
  dupes = (rows_with_dupes+cols_with_dupes-[{}])

  # we solve one row at a time
  first_row = dupes[0]

  if !first_row
    # no dupes => success!
    J.map{|m,v|k.member?(m)?v:?#}.each_slice(N){|s|puts s*''}
    exit
  else
    # the digit doesn't really matter
    t=first_row.values

    # cross-multiply all arrays in the row to get a
    # small search space. We choose one cell from each
    # digit grouping and drop the rest.
    t.inject(:product).map{ |*e|
      # Technically, we drop all cells, and add back the
      # chosen cells, but it's all the same.
      new_k = k-t.flatten+e.flatten

      # and then search that space, recursively
      Q[new_k]
    }
  end
end

Код виконується за допомогою:

# run with whole board
Q[K]

# if we get here, we didn't hit an exit, so we fail
puts"no answer"

Журнал змін

394 додав пропозицію @ blutorange нижче, і вирізав набагато більше маніпуляцій

408 ще раз переглянули вихід. Також використовуйте .minзамість того, що .inject(:+)я все одно беру один рядок.

417 коротший розрахунок випуску

421 знизили складні числа. Просто використовуйте цілі числа. Збережіть пакет

Ще 450 поліпшень введення

456 поліпшень введення

462 поступових поліпшень - особливо find, неselect

475 скинув uі розчавив будівельник дуп

503 вирішувати одночасно одну копію цифр на рядок / стовпець.

530 використання map &:popзамістьvalues

531 витягніть лямбда, що складає матовий масив

552 ой! пропустили вимогу

536 незначно поліпшена популяція мальпових масивів (що раніше d)

541 початковий


Всередині лямбда, які breakможна використовувати замість return, можна зберегти ще один байт. Мені дуже подобається цей, багато хитрощів і набагато швидше.
blutorange

@blutorange Дякую! Застосовується. Але все ж є способи перейти до 344, хоча.
Не те, що Чарльз

Трохи довше, так, але в іншому випадку це виглядає добре. Ще одне, що я бачив: у другому рядку, оскільки змінна aвикористовується лише один раз, ви могли це зробити J=gets(p).split*''. Ви пробували аргументи cli, дивіться мою відповідь?
blutorange

3

HTML + JavaScript ( ES6 ) 459

Використання текстової області HTML для отримання багаторядкового введення.

Щоб перевірити, запустіть фрагмент у Firefox. Якщо вам подобається, збільште текстову область, вставте вхід всередині текстової області (будьте обережні: точне введення даних, жодних зайвих пробілів у жодному рядку) та вкладку. Результат додається.

Метод: рекурсивна глибина першого серарху. Він знаходить неоптимальні рішення для всіх тестових випадків за лічені секунди (це gode golf, але для звичайних тестових випадків дійсна відповідь повинна закінчитися)

<textarea onchange="s=this.value,
  z=s[0],o=-~z,k=[],X='no answer',f='#',
  (R=(s,t,h={},r=[],d=0)=>(
    s.map((v,i)=>v>0&&[i%o,~(i/o)].map(p=>h[p+=v]=[...h[p]||[],i])),
    [h[i][1]&&h[i].map(p=>r[d=p]=p)for(i in h)],
    d?r.some(p=>(
      (s[p+o]!=f&s[p-o]!=f&s[p-1]!=f&s[p+1]!=f)&&
      (
        g=[...s],g[p]=f,e=[...g],n=0,
        (F=p=>e[p]>0&&[1,-1,o,-o].map(d=>F(p+d),e[p]=--n))(p<o?p+o:p-o),
        t+n==0&&!k[g]&&(k[g]=1,R(g,t-1))
      )
    )):X=s.join('')
  ))([...s.slice(2)],z*z-1),this.value+=`

`+X"></textarea>

Спочатку спробуйте

Метод: Перший серарх глибини, нерекурсивний, використовуючи стек користувача.
Функцію можна легко трансформувати в Breadth First Search, підключившись l.popдо цього l.shift, тому використовуючи чергу замість стека, але це занадто повільно, і я не впевнений, що все одно може знайти оптимальне рішення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.