Chương trình đu dây Olympic của Tarzan


32

Những người chơi đu dây Olympic thực hiện thói quen của họ trong những cây tiêu chuẩn. Cụ thể, Standard Tree ncó các đỉnh cho 0đến hết n-1và các cạnh liên kết từng đỉnh khác không avới đỉnh n % abên dưới nó. Vì vậy, ví dụ, Standard Tree 5 trông như thế này:

3
|
2   4
 \ /
  1
  |
  0

bởi vì phần còn lại khi 5 chia cho 3 là 2, phần còn lại khi 5 chia cho 2 hoặc 4 là 1 và phần còn lại khi 5 chia cho 1 là 0.

Năm nay, Tarzan sẽ bảo vệ số vàng của mình bằng các thói quen mới, mỗi lần bắt đầu từ đỉnh n - 1, dao động đến đỉnh n - 2, tiếp tục lên đỉnh n - 3, v.v., cho đến khi cuối cùng anh ta chuyển sang đỉnh 0.

Điểm cho một thói quen là tổng số điểm cho mỗi cú swing (bao gồm cả lần tháo gỡ) và điểm cho cú swing là khoảng cách trong cây giữa điểm bắt đầu và điểm kết thúc của nó. Do đó, thói quen của Tarzan trên Standard Tree 5 có điểm 6:

  • một cú swing từ 4để 3ghi ba điểm (xuống, lên, lên),
  • một cú swing từ 3để 2ghi một điểm (xuống),
  • một cú swing từ 2để 1ghi một điểm (xuống), và
  • một điểm hạ gục từ 1để 0ghi một điểm (xuống).

Viết chương trình hoặc hàm, với một số nguyên dương n, sẽ tính điểm của thói quen Tarzan trên Cây tiêu chuẩn n. Mẫu đầu vào và đầu ra:

 1 ->  0
 2 ->  1
 3 ->  2
 4 ->  6
 5 ->  6
 6 -> 12
 7 -> 12
 8 -> 18
 9 -> 22
10 -> 32
11 -> 24
12 -> 34
13 -> 34
14 -> 36
15 -> 44
16 -> 58
17 -> 50
18 -> 64
19 -> 60
20 -> 66
21 -> 78
22 -> 88
23 -> 68
24 -> 82

Quy tắc và tính điểm mã như bình thường đối với .


9
Tôi không tìm thấy trình tự này trong OEIS. Câu hỏi hay.
Leaky Nun

8
Thông số kỹ thuật tuyệt vời!
xnor

1
@LeakyNun Nó nên được thêm vào mặc dù. Đó là một chuỗi rất nguyên bản! (Ngay cả khi không có cơ sở)
DanTheMan

Câu trả lời:


12

C, 98 97 byte

F(i){int c[i],t=i-2,n=0,p;for(;++n<i;)for(p=c[n]=n;p=i%p;c[p]=n)t+=c[p]<n-1;return i>2?t*2:i-1;}

Điều này sẽ tính toán khoảng cách giữa mỗi cặp điểm với công thức sau:

  • Thêm khoảng cách từ gốc đến nút A
  • Thêm khoảng cách từ gốc đến nút B
  • Trừ 2 * chiều dài gốc chung của A và B

Điều này có lợi thế là khi áp dụng cho tất cả các cặp, nó giống như:

  • Thêm 2 * khoảng cách từ gốc đến mỗi nút
  • Trừ 2 * chiều dài của gốc chung của mỗi cặp nút
  • Trừ khoảng cách từ gốc đến nút đầu tiên
  • Trừ khoảng cách từ gốc đến nút cuối cùng

Để làm cho logic đơn giản hơn, chúng tôi giả sử chúng ta đi từ nút 0 đến nút n-1, thay vì n-1 đến 0 như trạng thái câu hỏi. Khoảng cách từ nút gốc đến nút 0 rõ ràng là 0 (chúng giống nhau). Và chúng ta có thể thấy rằng đối với (hầu hết) cây, khoảng cách từ nút cuối cùng đến gốc là 2:

                    n+1 % n = 1  for all n > 1
and:                  n % 1 = 0  for all n >= 0
therefore:  n % (n % (n-1)) = 0  for all n > 2

Điều này có nghĩa là chúng tôi có một số trường hợp đặc biệt (n <2) nhưng chúng tôi có thể giải thích cho những trường hợp đó đủ dễ dàng.

Phá vỡ:

F(i){                               // Types default to int
    int c[i],                       // Buffer for storing paths
        t=i-2,                      // Running total score
        n=0,                        // Loop index
        p;                          // Inner loop variable
    for(;++n<i;)                    // Loop through all node pairs (n-1, n)
        for(p=c[n]=n;p=i%p;c[p]=n)  //  Recurse from current node (n) to root
            t+=c[p]<n-1;            //   Increase total unless this is a common
                                    //   node with the previous path
    return i>2?   :i-1;             // Account for special cases at 1 and 2
               t*2                  // For non-special cases, multiply total by 2
}

Cảm ơn @feersum vì đã lưu 1 byte


Tiền thưởng: Cây xanh!

Tôi đã viết một chương trình nhanh và bẩn để xem những cái cây này trông như thế nào. Đây là một số kết quả:

6:

5 4  
| |  
1 2 3
 \|/ 
  0  

8:

  5      
  |      
7 3   6  
|  \ /   
1   2   4
'--\|/--'
    0    

13:

   08              
    |              
11 05   10 09 07   
 |   \ /    |  |   
02   03    04 06 12
 '-----\  /---'--' 
        01         
         |         
        00         

19:

   12                       
    |                       
   07   14                  
     \ /                    
     05    15 11            
       \  /    |            
17      04    08 16 13 10   
 |       '-\  /--'   |  |   
02          03      06 09 18
 '---------\ |/-----'--'--' 
            01              
             |              
            00              

49:

                         31                                                    
                          |                                                    
           30            18   36                                               
            |              \ /                                                 
           19   38 27      13    39 29    32                                   
             \ /    |        \  /    |     |                                   
   26        11    22 44      10    20 40 17   34                              
    |         '-\  /--'        '-\  /--'    \ /                                
47 23   46       05               09        15    45 43 41 37 33 25    35 28   
 |   \ /          '--------------\ |/-------'-----'   |  |  |  |  |     |  |   
02   03                           04                 06 08 12 16 24 48 14 21 42
 '----'--------------------------\ |/----------------'--'--'--'--'--'    \ |/  
                                  01                                      07   
                                   '-----------------\  /-----------------'    
                                                      00                       

Có một số dấu ngoặc đơn thừa trong câu lệnh return.
frageum

@feersum d'oh! Chúng không phải lúc nào cũng thừa, nhưng sau đó tôi đã thay đổi cách xử lý trường hợp đặc biệt. Cảm ơn!
Dave

3
Yêu những hình dung!
Edward

7

Python 2, 85 byte

def f(a,i=1):h=lambda n:n and{n}|h(a%n)or{0};return i<a and len(h(i)^h(i-1))+f(a,i+1)

7

Perl, 65 59 55 54 byte

Bao gồm +2 cho -ap

Chạy với kích thước cây trên STDIN:

for i in `seq 24`; do echo -n "$i: "; vines.pl <<< $i; echo; done

vines.pl:

#!/usr/bin/perl -ap
$_=map{${"-@F"%$_}|=$_=$$_|$"x$p++.1;/.\b/g}1-$_..-1

Giải trình

Nếu bạn viết lại cây

3
|
2   4
 \ /
  1
  |
  0

đến một nơi mà mỗi nút chứa tập hợp của tất cả tổ tiên và chính nó:

 {3}
  |
{2,3}   {4}
   \    /
    \  /
  {1,2,3,4}
      |
 {0,1,2,3,4}

Sau đó, chúng ta có thể mô tả ví dụ: tất cả các nút đường dẫn từ 4 đến 3 là:

  • Tất cả các nút có chứa 3 nhưng không phải 4 (đi xuống từ 3)
  • Tất cả các nút có chứa 4 nhưng không phải 3 (đi xuống từ 4)
  • Nút cao nhất chứa cả 3 và 4 (nối)

Số cạnh ít hơn một số nút nên chúng ta có thể sử dụng số đó để bỏ qua điểm nối, vì vậy số cạnh trên đường dẫn từ 4 đến 3 là 3 vì:

  • Số lượng nút chứa 3 nhưng không phải nút 4: 2
  • Số lượng nút chứa 4 nhưng không phải nút 3: 1

Lưu ý rằng điều này cũng hoạt động đối với một đường dẫn trực tiếp đến mục tiêu của nó, ví dụ: đối với đường dẫn từ 3 đến 2, số cạnh là 1 vì:

  • Số lượng nút chứa 2 nhưng không phải nút 3: 0
  • Số lượng nút chứa 3 nhưng không phải nút 2: 1

Sau đó chúng ta có thể tổng hợp tất cả các kết hợp này.

Thay vào đó, nếu bạn chỉ nhìn vào một nút, ví dụ nút 2 với tập hợp tổ tiên {2,3}. Nút này sẽ đóng góp một lần khi xử lý đường dẫn 2 to 1vì nó chứa 2 nhưng không phải là 1. Nó sẽ không đóng góp gì cho đường dẫn 3 to 2vì nó có cả 2 và 3, nhưng nó sẽ đóng góp một lần khi xử lý đường dẫn 4 to 3vì nó có 3 nhưng không 4. Nói chung, một số trong tập hợp tổ tiên của một nút sẽ đóng góp một cho mỗi hàng xóm (một mức thấp hơn cao hơn) không có trong tập hợp. Ngoại trừ yếu tố tối đa (4 trong trường hợp này) chỉ đóng góp cho hàng xóm thấp 3 vì không có đường dẫn5 to 4. Simular 0 là một mặt, nhưng vì 0 luôn nằm ở gốc cây và chứa tất cả các số (đó là phép nối cuối cùng và chúng ta không đếm số lần tham gia) nên không bao giờ có bất kỳ đóng góp nào từ 0 nên dễ dàng nhất là chỉ để lại nút 0 hoàn toàn

Vì vậy, chúng ta cũng có thể giải quyết vấn đề bằng cách xem tập hợp tổ tiên cho mỗi nút, đếm các đóng góp và tổng trên tất cả các nút.

Để dễ dàng xử lý hàng xóm, tôi sẽ biểu diễn tập hợp tổ tiên dưới dạng một chuỗi khoảng trắng và 1 trong đó mỗi 1 ở vị trí p biểu thị rằng n-1-p là tổ tiên. Vì vậy, ví dụ trong trường hợp n=51 của chúng tôi tại vị trí 0 chỉ ra 4 là tổ tiên. Tôi sẽ rời khỏi không gian dấu. Vì vậy, đại diện thực tế của cây tôi sẽ xây dựng là:

" 1"
  |
" 11"   "1"
   \    /
    \  /
   "1111"

Lưu ý rằng tôi đã bỏ qua nút 0 sẽ được đại diện bởi "11111"vì tôi sẽ bỏ qua nút 0 (nó không bao giờ đóng góp).

Tổ tiên không có hàng xóm thấp hơn hiện được biểu thị bằng phần cuối của chuỗi 1. Tổ tiên không có hàng xóm cao hơn hiện được biểu thị bằng bắt đầu chuỗi 1, nhưng chúng ta nên bỏ qua bất kỳ bắt đầu nào của chuỗi khi bắt đầu chuỗi vì điều này sẽ đại diện cho đường dẫn 5 to 4không tồn tại. Sự kết hợp này hoàn toàn phù hợp với regex /.\b/.

Việc xây dựng chuỗi tổ tiên được thực hiện bằng cách xử lý tất cả các nút theo thứ tự n-1 .. 1và ở đó đặt 1 ở vị trí cho chính nút đó và thực hiện "hoặc" vào hậu duệ.

Với tất cả những gì chương trình đủ dễ hiểu:

-ap                                                  read STDIN into $_ and @F

   map{                                    }1-$_..-1 Process from n-1 to 1,
                                                     but use the negative
                                                     values so we can use a
                                                     perl sequence.
                                                     I will keep the current
                                                     ancestor for node $i in
                                                     global ${-$i} (another
                                                     reason to use negative
                                                     values since $1, $2 etc.
                                                     are read-only
                       $$_|$"x$p++.1                 "Or" the current node
                                                     position into its ancestor
                                                     accumulator
                    $_=                              Assign the ancestor string
                                                     to $_. This will overwrite
                                                     the current counter value
                                                     but that has no influence
                                                     on the following counter
                                                     values
       ${"-@F"%$_}|=                                 Merge the current node
                                                     ancestor string into the
                                                     successor
                                                     Notice that because this
                                                     is an |= the index
                                                     calculation was done
                                                     before the assignment
                                                     to $_ so $_ is still -i.
                                                     -n % -i = - (n % i), so
                                                     this is indeed the proper
                                                     index
                                     /.\b/g          As explained above this
                                                     gives the list of missing
                                                     higher and lower neighbours
                                                     but skips the start
$_=                                                  A map in scalar context
                                                     counts the number of
                                                     elements, so this assigns
                                                     the grand total to $_.
                                                     The -p implicitly prints

Lưu ý rằng thay thế /.\b/bằng cách /\b/giải quyết phiên bản tròn của vấn đề này, nơi tarzan cũng đi theo con đường0 to n-1

Một số ví dụ về cách các chuỗi tổ tiên trông (được đưa ra theo thứ tự n-1 .. 1):

n=23:
1
 1
  1
   1
    1
     1
      1
       1
        1
         1
          1
          11
         1  1
        1    1
       1      1
      11      11
     1          1
    11  1    1  11
   1              1
  1111  11  11  1111
 111111111  111111111
1111111111111111111111
edges=68

n=24:
1
 1
  1
   1
    1
     1
      1
       1
        1
         1
          1
           1
          1 1
         1   1
        1     1
       1       1
      1         1
     1  1     1  1
    1             1
   11    1   1    11
  1   1         1   1
 1        1 1        1
1                     1
edges=82

Rất tiếc, tôi không nhận ra bản chỉnh sửa của bạn chỉ mới vài giây. Dù sao, cách tiếp cận và giải thích rất gọn gàng!
FryAmTheEggman

@FryAmTheEggman Không có vấn đề gì, chúng tôi chỉ sửa vấn đề bố cục chính xác. Dù sao, vâng, tôi khá hài lòng với cách tất cả các phần kết hợp với nhau trong chương trình này. Hiện tại tôi không thấy có bất kỳ chất béo nào được cắt giảm ..
TonMedel 24/8/2016

3

Toán học, 113 103 102 byte

(r=Range[a=#-1];Length@Flatten[FindShortestPath[Graph[Thread[r<->Mod[a+1,r]]],#,#2]&@@{#,#-1}&/@r]-a)&

-10 byte nhờ @feersum; -1 byte nhờ @MartinEnder

Sau đây là xa nhanh hơn (nhưng lâu hơn, không may, tại 158 byte ):

(a=#;If[a<4,Part[-{1,1,1,-6},a],If[EvenQ@a,-2,1]]+a+4Total[Length@Complement[#,#2]&@@#&/@Partition[NestWhileList[Mod[a,#]&,#,#!=0&]&/@Range@Floor[a/2],2,1]])&

Tôi tin rằng bạn có thể chỉ định mọi thứ mà không cần sử dụng With. Ngoài ra, có vẻ như mỗi lần Rangeđược sử dụng, alà đối số, do đó có thể được đưa ra.
frageum

1
r=Range[a=#-1]tiết kiệm một byte.
Martin Ender

2

J, 37 byte

[:+/2(-.+&#-.~)/\|:@(]|~^:(<@>:@[)i.)

Sử dụng:

   f=.[:+/2(-.+&#-.~)/\|:@(]|~^:(<@>:@[)i.)
   f 10
32
   f every 1+i.20
0 1 2 6 6 12 12 18 22 32 24 34 34 36 44 58 50 64 60 66

Hãy thử trực tuyến tại đây.


Tôi muốn biết chi tiết về cách thức hoạt động của nó. Ngoài ra, dịch vụ tryj.tk dường như đã bị hỏng ("không thể đọc localStorage '" và "$ (khóa) .terminal không phải là một chức năng")
Dave

@Dave trang web đó không hoạt động với tôi trên Chrome, nhưng hoạt động nếu tôi thử sử dụng IE hoặc Edge, tuy nhiên tôi khuyên bạn nên cài đặt J ( link ) nếu bạn quan tâm đến nó!
dặm

@miles Weird, đối với tôi nó hoạt động cho tất cả các trình duyệt (FF, Chrome, IE).
ngẫu nhiên

Nó đã làm việc cho tôi sử dụng Chrome, nhưng nó ngừng làm việc một vài tháng trước và bắt đầu phản ứng với một thông báo lỗi tương tự như của Dave
dặm

@Edward Sẽ làm gì khi tôi tìm thấy một chút thời gian.
ngẫu nhiên

1

JavaScript (ES6), 118 116 byte

n=>[...Array(n)].map(g=(_,i)=>i?[...g(_,n%i),i]:[],r=0).reduce(g=(x,y,i)=>x.map(e=>r+=!y.includes(e))&&i?g(y,x):x)|r

Thiếu một hàm khác biệt thực sự gây tổn thương, nhưng một số đệ quy sáng tạo làm giảm số lượng byte một chút. Chỉnh sửa: Đã lưu 2 byte bằng cách xóa một tham số không cần thiết.

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.