Chọn lọc số nguyên dương


21

Giới thiệu

Số học Gaol là một cơ sở đặc biệt bao gồm các số nguyên dương. Tuy nhiên, gần đây, các số nguyên tích cực đã cố gắng trốn thoát. Do đó, các phường đã quyết định, ừm, loại bỏ một số số nguyên dương để gửi tin nhắn đến các số nguyên khác. Họ đã thuê một kỹ sư phần mềm để viết một chương trình để tìm ra số nguyên nào cần loại bỏ để đạt hiệu quả tối đa.

Mô tả đầu vào

Đầu vào được đưa ra thông qua STDIN, đối số dòng lệnh hoặc chức năng nhập của người dùng (chẳng hạn như raw_input). Bạn không thể có nó như là một đối số chức năng hoặc một biến được khởi tạo trước (ví dụ: chương trình này mong đợi đầu vào trong một biến x).

Dòng đầu tiên chứa một số nguyên dương duy nhất ntrong đó 8 >= n >= 3. Theo sau đó là ncác dòng chứa các nký tự từ bộ [1,2,3,4,5,6,7,8,9]. Đây là một ví dụ đầu vào:

5
22332
46351
65455
24463
65652

Mô tả đầu ra

Các phường muốn loại bỏ các số để đáp ứng các điều kiện sau:

  • Trong mỗi hàng và cột của lưới kết quả, không có số nào xuất hiện hai lần;
  • Không có hai số bị loại có thể được liền kề theo chiều ngang hoặc chiều dọc;
  • Các số còn sống phải tạo thành một nhóm liền kề trực giao - có thể đi từ bất kỳ số còn sống nào sang bất kỳ số còn sống nào khác chỉ di chuyển theo chiều ngang và chiều dọc và không bao giờ vượt qua bất kỳ số bị loại bỏ nào.

Đầu ra đầu vào (trừ dòng đầu tiên), với các số bị loại bỏ được thay thế bằng #.

Có thể có nhiều hơn một giải pháp. Nếu đó là trường hợp, bạn có thể xuất bất kỳ giải pháp.

Cũng có thể không có giải pháp. Nếu đó là trường hợp, đầu ra chuỗi no answer.

Đây là một đầu ra có thể cho đầu vào ví dụ:

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

Ví dụ đầu vào và đầu ra

Có nhiều đầu ra cho mỗi đầu vào, vì vậy những đầu ra này chỉ là ví dụ.

Đầu vào:

5
46551
51565
32654
14423
43244

Đầu ra:

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

Đầu vào:

7
7183625
1681563
5238564
8786268
1545382
3814756
5325345

Đầu ra:

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

Đầu vào:

8
21534768
75196287
68392184
96244853
44865912
76516647
89751326
43698979

Đầu ra:

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

Đầu vào:

4
2222
2331
3112
1322

Đầu ra:

no answer

4
(Còn được gọi là Người độc thân .)
Doorknob

Câu đố này là tuyệt vời. Cảm ơn bạn. Làm việc trên một giải pháp
Không phải là Charles

1
Tôi thích câu đố này, nhưng nó không thể được trả lời "như hiện tại" bằng cách sử dụng JavaScript trong trình duyệt, vì phương thức nhập liệu duy nhất của người dùng promptkhông cho phép nhập nhiều dòng.
edc65

Câu trả lời:


0

Ruby, 346 344 329 316 byte, slw

Đây là môn đánh gôn, không nhanh bằng mã, nên ...

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"

Sử dụng nó như:

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

Cờ -W0không cần thiết, nhưng tôi khuyên bạn nên thêm nó để tắt cảnh báo hoặc chuyển hướng stderr...

Hãy cho tôi biết nếu bạn có đủ kiên nhẫn để chạy nó trên các ví dụ cho n = 6,7,8

Thay đổi

  • eachmap, nhờ @NotThatCharles
  • kiểm tra các lần xóa liền kề và cùng một chữ số theo hai regexps, tương tự như những gì @NotThatCharles đã đề xuất
  • tối ưu hóa đọc đầu vào một chút
  • nhỏ hơn và chậm hơn

một vài cải tiến gia tăng về kích thước: \dngắn hơn so với [^#]dòng áp chót; M.times.to_adài hơn(0..M-1).to_a
Không phải Charles

@NotThatCharles Cảm ơn lời khuyên! Nhưng ngắn hơn M.times.to_a1 byte so với ;) cũng hoạt động và có cùng độ dài. (0..M-1).to_a(0...M).to_a
blutorange

... đếm rất khó
Không phải Charles

nó thực sự hoàn thành khi n = 8?
Không phải Charles

@NotthatCharles Nếu bạn đợi loooong đủ, hoặc sử dụng PC siêu nhanh, vâng, nó nên như vậy. Đây là môn đánh gôn không có bất kỳ hạn chế nào về tốc độ, vì vậy tôi đã ưu tiên độ dài mã ...
blutorange

5

Ruby - 541 ..., 394

Thuật toán cơ bản là một tìm kiếm theo chiều sâu đệ quy đầu tiên của các bản sao để chọn một cách khẳng định, xem qua hàng 1, rồi cột 1, rồi hàng 2, v.v. và kiểm tra xem hai hàng xóm không bị giết và lưới được kết nối (đó là break ifmệnh đề trong ở đó, và bit đến trước nó).

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"

Một số thủ thuật gọn gàng:

if w[1]ngắn hơn nhiều if !w.one?và nếu bạn biết có ít nhất một thành viên, đó là kết quả tương tự.

Tương tự, [0]ngắn hơn any?nếu không có khối và s[j]là một phím tắt dễ thương cho j<s.size(về mặt kỹ thuật, nó giống như j.abs<s.size)

y%N+(y/N).ingắn hơn nhiều so vớiComplex(y%N,y/N)

Ngoài ra, khi có hai điều kiện phức tạp để tạo chuỗi, có thể sẽ ngắn [cond1?str1a:str1b,cond2?str2a:str2b]*''hơn so với việc thêm tất cả các parens hoặc #{}s vào.

Ungolfing và giải thích:

(Đây là từ phiên bản 531 byte. Tôi đã thực hiện các thay đổi. Đáng chú ý nhất là tôi đã loại bỏ lệnh gọi sản phẩm - chỉ giải quyết một chữ số trên mỗi hàng / cột và J hiện chỉ là một mảng, được lập chỉ mục bởi số nguyên. Tất cả các tọa độ chỉ là số nguyên.)

H tính toán hàng xóm

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 là kích thước của lưới

N = gets.to_i

K là các phím của bản đồ (số phức)

# 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 là bản đồ đầu vào (tù)

# 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 là các phím không bị giết

# starts as K

Q là phương pháp đệ quy chính

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

Mã được thực thi với:

# run with whole board
Q[K]

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

Thay đổi

394 đã thêm đề xuất của @ blutorange bên dưới và cắt ra nhiều thao tác hơn

408 điều chỉnh đầu ra một lần nữa. Cũng sử dụng .minthay vì .inject(:+)kể từ khi tôi chỉ lấy một hàng.

Tính toán đầu ra ngắn hơn 417

421 giảm số phức. Chỉ cần sử dụng số nguyên. Lưu một bó

Thêm 450 cải tiến đầu vào

456 cải tiến đầu vào

462 cải tiến gia tăng - đặc biệt. find, không phảiselect

475 rơi uvà đè bẹp người xây dựng hàng / col dupe

503 chỉ giải quyết một chữ số trùng lặp trên mỗi hàng / cột tại một thời điểm.

530 sử dụng map &:popthay vìvalues

531 kéo ra lambda làm cho mảng dupes

552 oái oăm! bỏ lỡ một yêu cầu

536 dân số được cải thiện một chút về mảng dupes (trước đây là d)

541 ban đầu


Bên trong lambdas, breakcó thể được sử dụng thay vì return, có thể tiết kiệm thêm một byte. Tôi thực sự hiểu điều này, rất nhiều mánh khóe và nhanh hơn nhiều.
blutorange

@blutorange Cảm ơn! Áp dụng. Vẫn có một cách để đi đến 344, mặc dù.
Không phải Charles

Một chút nữa, có, nhưng nếu không nó có vẻ được thực hiện tốt. Một điều nữa tôi thấy: Trong dòng thứ hai, vì biến achỉ được sử dụng một lần, bạn có thể thực hiện điều đó J=gets(p).split*''. Bạn đã thử cli đối số, xem câu trả lời của tôi?
blutorange

3

HTML + JavaScript ( ES6 ) 459

Sử dụng một văn bản HTML để có được đầu vào đa dòng.

Để kiểm tra, hãy chạy đoạn mã trong Firefox. Phóng to textarea nếu bạn thích, dán đầu vào bên trong textarea (coi chừng: đầu vào chính xác, không có khoảng trắng thừa trên bất kỳ dòng nào) và bỏ qua tab. Kết quả được nối thêm.

Phương pháp: một đệ quy sâu đầu tiên. Nó tìm thấy các giải pháp không tối ưu cho tất cả các trường hợp thử nghiệm trong vài giây (đó là môn đánh gôn, nhưng câu trả lời hợp lệ sẽ chấm dứt đối với các trường hợp thử nghiệm thông thường)

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

Lần thử đầu tiên

Phương thức: một Serarch First Depth, không đệ quy, sử dụng ngăn xếp người dùng.
Các chức năng có thể dễ dàng chuyển đổi trong một Chiều rộng tìm kiếm đầu tiên, chaging l.popđến l.shift, vì vậy sử dụng một hàng đợi thay vì một chồng, nhưng nó quá chậm và tôi không chắc chắn rằng nó có thể tìm thấy một giải pháp tối ưu anyway.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.