Apakah itu Pohon Linier? (Edisi Lebar-pertama)

11

Latar Belakang

Pohon tanpa label mungkin terlihat seperti ini:

   o
 / | \
o  o  o
|    / \
o   o   o

Untuk merender pohon ini, pertama-tama kita beri label pada setiap simpul odengan jumlah simpul turunannya:

   3
 / | \
1  0  2
|    / \
0   0   0

dan kemudian tulis angka-angka dalam daftar dengan cara yang menarik napas pertama, artinya baris demi baris dan kiri ke kanan:

[3, 1, 0, 2, 0, 0, 0]

Ini adalah representasi unik dan tidak ambigu dari pohon di atas, yang berarti bahwa tidak ada dua pohon murni yang berbeda akan memiliki linierisasi yang sama dan bahwa kita dapat merekonstruksi pohon asli dari daftar.

Meskipun setiap pohon sesuai dengan daftar bilangan bulat tertentu, tidak setiap daftar bilangan bulat mewakili pohon linier yang valid: Sebagai contoh [2, 0, 0, 0]tidak mewakili pohon yang valid, jika kita mencoba untuk de-linierisasi itu kita berakhir dengan pohon ini

[2,0,0,0] -> 2 [0,0,0] -> 2 [0,0] -> 2 [0]
            / \          / \        / \
                        0          0   0

tetapi masih memiliki 0kiri dalam daftar dan tidak ada tempat untuk meletakkannya. Demikian pula [2, 0]juga bukan linierisasi pohon yang valid, karena pohon de-linierisasi memiliki bercak anak kosong:

  2
 / \
0

Tugas

Diberikan daftar integer, putuskan apakah itu linearisasi pohon yang valid menggunakan sesedikit mungkin byte. Anda dapat menulis program atau fungsi lengkap.

Input: Daftar bilangan bulat non-kosong yang tidak kosong.

Output: Nilai kebenaran jika daftar adalah linierisasi pohon, nilai palsu sebaliknya.

Testcases

Sejujurnya
[0]
[2, 0, 0]
[1, 1, 1, 1, 1, 0]
[3, 1, 0, 2, 0, 0, 0]
[2, 0, 2, 2, 0, 0, 2, 0, 0]
[3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0]
[1, 5, 3, 0, 2, 1, 4, 0, 1, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 2, 1, 0, 0, 1, 0]
Falsy
[0, 1]
[2, 0]
[2, 0, 0, 0]
[1, 0, 1]
[3, 2, 1, 0]
[2, 0, 0, 2, 0, 0]
[4, 1, 0, 3, 0, 0, 0, 0]
[4, 2, 0, 3, 1, 0, 0, 0, 0, 0]
Laikoni
sumber

Jawaban:

4

Haskell, 44 byte

f[n:k]=iterate f[k]!!n
f _=[]
g x=f[x]==[[]]

Menentukan fungsi gyang mengambil daftar dan mengembalikan boolean. Lihat lulus semua kasus uji .

Penjelasan

Ini bergantung pada fakta bahwa linearisasi mendalam-pertama dan luas-pertama menghasilkan array yang sama. Lihat jawaban Martin untuk detailnya; pada dasarnya mereka berdua memberikan kondisi aritmatika yang sama pada array.

Fungsi fini diberikan daftar input yang dibungkus dalam daftar tunggal. Ini muncul satu nomor ndari daftar, kemudian memanggil dirinya sendiri nkali pada daftar yang tersisa untuk memproses anak-anak dari simpul muncul (kedalaman pertama). Memunculkan daftar kosong menghasilkan [], yang saya gunakan sebagai status kesalahan. Fungsi gmemeriksa bahwa hasil akhirnya adalah [[]], kondisi unik yang tidak salah tanpa node yang tidak diproses. Jika Haskell diketik dengan lemah, saya hanya bisa menggunakan 0atau sesuatu sebagai status kesalahan, dan tidak harus memasukkan input ke daftar lain.

Zgarb
sumber
3

Mathematica, 38 byte

Last@#<0<=Min@Most@#&@Accumulate[#-1]&

Ide dasarnya adalah bahwa kami melacak sejumlah node yang harus diisi. Setiap elemen dalam daftar menggunakan satu simpul dan menambahkan sebanyak yang dimiliki anak-anak. Jadi setiap elemen imengubah jumlah total oleh i-1. Hitungan ini dimatikan satu per satu, karena harus dimulai dari 1(root), bukan 0.

Agar pohon valid kita a) tidak pernah bisa pergi di bawah 0seluruh daftar, karena kita tidak akan punya tempat untuk menempatkan node saat ini dan b) harus berakhir dengan -1pada akhirnya, kalau tidak kita punya node yang tidak digunakan tersisa.

Kami memperoleh total jumlah node yang tersisa ini Accumulate[#-1](yang menghitung jumlah awalan dari daftar input minus satu). Dan kemudian kita periksa apakah elemen terakhir dan hanya elemen terakhir -1dengan:

Last@#<0<=Min@Most@#

Perhatikan bahwa memeriksa elemen terakhir adalah negatif sudah cukup, karena kita tidak pernah bisa mengurangi lebih dari 1, jadi jika nilai terakhir adalah -2atau lebih rendah maka tidak mungkin untuk minimum yang lain menjadi non-negatif.

Martin Ender
sumber
2

Retina , 29 byte

\d+
$*
^(?<-1>(1)*,)*$(?(1)!)

Cobalah online! (Baris pertama memungkinkan suite tes yang dipisahkan dengan linefeed.)

Penjelasan

Ide dasarnya sama dengan jawaban Mathematica saya : kami melacak jumlah node yang tersisa, memastikan tidak pernah berjalan di bawah nol tetapi berakhir di nol. Namun, cara ini diterapkan dengan regex sangat berbeda.

\d+
$*

Ini hanya mengubah input ke unary, mengubah setiap integer nmenjadi n1s.

^(?<-1>(1)*,)*$(?(1)!)

Di sinilah keajaiban yang sebenarnya terjadi. Ini regex yang cukup pendek yang hanya cocok dengan pohon yang valid, tetapi mekanismenya cukup halus.

Saya menggunakan grup balancing untuk melacak jumlah node, yang merupakan cara untuk bekerja dengan tumpukan di dalam regex.

Pertama, tentu saja tumpukan seperti itu tidak akan pernah memiliki kedalaman negatif, jadi kita tidak bisa benar-benar berakhir dengan representasi -1pada akhirnya, seperti yang kita lakukan dalam solusi Mathematica. Namun, kita dapat mencatat bahwa elemen terakhir dari input memiliki menjadi nol pada tumpukan valid (jika tidak kita tidak bisa berakhir dengan -1). Ternyata bahwa itu benar-benar menghemat byte untuk memeriksa kedua yang kami berakhir pada nol dan dengan nol node yang tersisa.

Jadi di sini adalah rincian dari regex:

^        e# Anchor the match to the beginning of the string.
(?<-1>   e# Each repetition of this group will match one number. 
         e# We can ignore the <-1> for now.
  (1)*   e#   Match each unary digit of the current number, pushing
         e#   a capture onto stack 1. This increments our total of
         e#   remaining nodes by 1 for each child.
  ,      e#   Match a comma. Note that this requires that there is at
         e#   least one more number in the list.
)*       e# At the end of the repetition the <-1> pops one capture from
         e# the stack. This is the node that the current number itself
         e# takes up.
$        e# Match the end of the string. This requires the input to end
         e# in a zero, because the last thing we matched was a comma.
(?(1)!)  e# Make sure that stack 1 is empty, so that we don't have any
         e# unused nodes.
Martin Ender
sumber
1

CJam (20 byte)

{X0@{+\(_0>{\}*}/|!}

Secara online test suite . Ini adalah blok anonim yang mengambil array di stack dan meninggalkan 0 atau 1 di stack.

Pembedahan

Dalam pseudocode ini adalah:

p = 1
q = 0
foreach (i in input):
  q += i
  if (--p <= 0):      # in practice, if (--p == 0):
      p, q = q, p
return (p | q) == 0   # i.e. p == 0 && q == 0

qmengakumulasi jumlah label dari node pada level saat ini di pohon; pmenghitung mundur node yang tersisa di level saat ini.

Peter Taylor
sumber
{X0@{+\(_{\}&}/|!}Kupikir?
Martin Ender
Juga sepertinya Anda harus dapat menyimpan byte dengan menggunakan program lengkap untuk menghindari @.
Martin Ender
1

Labirin , 17 byte

(
+?
;-)
,_"
@@,!

Cobalah online!

Output yang benar adalah -1dan output yang salah kosong. Mendefinisikan kebenaran dan kepalsuan di Labyrinth agak sulit, karena cabang-cabang Labyrinth pada dasarnya adalah ternary. Namun, satu-satunya cara untuk membangun sebuah kondisional dengan dua cabang andal, Anda hanya dapat melakukan ini:

>"F
 T

Dalam hal ini, saya akan mempertimbangkan untuk bergerak lurus ke depan falsy (karena arah gerakan tidak terpengaruh) dan mengubah kebenaran. Ini sesuai dengan nol dan non-nol masing-masing. Alasan saya menggunakan output kosong untuk mewakili nol adalah bahwa, jika Anda mem-pipe output kembali ke program Labirin lain, operator input ?akan benar-benar mendorong nol jika input kosong, jadi saya menganggap string kosong valid representasi nol.

Penjelasan

Algoritma ini masih sama dengan jawaban Mathematica dan Retina saya, tetapi karena aliran kontrol Labyrinth, kali ini kerjanya sedikit berbeda:

  • Kami tidak bekerja dengan penghitung total secara terpisah di sini. Alih-alih, kami a) bekerja dengan penghitung negatif dan b) menginisialisasinya -11semula, sehingga kami ingin penghitung negatif di seluruh daftar dan mencapai nol pada input terakhir. Ini sebenarnya menyederhanakan aliran kontrol di sini.
  • Alih-alih membuat daftar lengkap dan memeriksa apakah mengandung nilai yang salah, ada tiga kemungkinan kondisi pemutusan:

    1. Kami menekan EOF sebelum mencapai jumlah total nol. Dalam hal ini, ada simpul yang tidak digunakan yang tersisa dan kami tidak mencetak apa pun.
    2. Kami mencapai nol dan kami berada di EOF. Dalam hal ini, kami memiliki pohon yang valid.
    3. Kami mencapai nol dan belum mencapai EOF. Dalam hal ini, kami kehabisan node sebelum mencakup semua elemen, dan kami tidak mencetak apa pun.

Adapun kode aktual, kita mulai di sudut kiri atas. The (ternyata nol implisit di atas tumpukan ke dalam -1, yang akan menjadi total berjalan. Kami kemudian memasuki loop utama yang sangat ketat dari program +?-)"_,;+,:

+   Add the top two values. This does nothing on the first iteration,
    but gets rid of a helper-zero on subsequent iterations.
?   Read and push integer.
-   Subtract it from running total.
)   Increment.
"   No-op. There is a branch at this point. If the running total is zero,
    we move straight ahead onto the , (see below). Otherwise, the loop continues.
_   Push a zero. This is necessary to prevent the IP from turning south.
,   Read a character. This will either be the next separator (some positive
    number) or EOF (-1). If it's EOF, the IP turns south and the program
    terminates. Otherwise, the loop continues.
;   Discard the separator.

Yang tersisa hanya kasus di mana kami telah mengurangi total berjalan ke nol di beberapa titik. IP bergerak ke kanan bawah ,dan membaca karakter lain untuk memeriksa apakah kita telah mencapai EOF. Jika tidak, nilainya akan positif dan IP berbalik ke barat menuju @dan program berakhir. Jika kami mencapai EOF, IP berbelok ke timur dan mencetak -1dengan !. IP kemudian akan mencari jalan ke kiri bawah @melalui jalan yang sedikit aneh untuk menghentikan program.

Martin Ender
sumber
0

Python, 82 byte

lambda l:len(l)==sum(l)+1 and not any(list(l[x]>=len(l)-x for x in range(len(l))))

Perlu lebih banyak test case.

Sparr
sumber
Anda tidak perlu menggunakan dengan listini jika ini adalah Python 2 setidaknya, dan dengan menata ulang dan membalikkan kondisi kedua Anda bisa mendapatkannya hingga 70 byte:lambda l:all(l[x]<len(l)-x for x in range(len(l)))and len(l)==sum(l)+1
Kade
^ Sehubungan dengan ini, Anda dapat mengubah tubuh allmenjadi x<len(l)-y for y,x in enumerate(l)untuk menyimpan 2 byte lagi untuk mendapatkannya menjadi 68.
Kade
Saya tidak bermain golf ini lebih jauh sekarang karena saya pikir itu bukan solusi yang akurat. Terima kasih atas tipsnya.
Sparr
0

Pyth, 13 byte

qxsM._tMQ_1tl

Kami mulai dengan menghitung kepenuhan pohon saat ini di semua titik dalam representasi input. Bagian dari gagasan itu sebagian besar dipinjam dari Martin Ender, jadi terima kasih padanya.sM._tMQ

Setelah kami memiliki daftar ini, kami memeriksa apakah indeks pertama yang berisi -1( x..._1) adalah panjang dari input minus satu ( q...tl(Q)).

Tidak percaya itu berhasil? Cobalah sendiri!

Steven H.
sumber