ทำคะแนนเส้นทางการแกว่งเถาไวน์ของทาร์ซาน


32

เถาองุ่นโอลิมปิกดำเนินการตามปกติในต้นไม้มาตรฐาน โดยเฉพาะอย่างยิ่งแผนภูมิมาตรฐานnมีจุดยอดสำหรับการ0ขึ้นn-1และขอบการเชื่อมโยงจุดยอดที่ไม่ใช่ศูนย์แต่ละจุดaกับจุดยอดn % aด้านล่าง ตัวอย่างเช่นต้นไม้มาตรฐาน 5 มีลักษณะดังนี้:

3
|
2   4
 \ /
  1
  |
  0

เนื่องจากส่วนที่เหลือเมื่อ 5 ถูกหารด้วย 3 คือ 2 ส่วนที่เหลือเมื่อ 5 ถูกหารด้วย 2 หรือ 4 คือ 1 และส่วนที่เหลือเมื่อ 5 ถูกหารด้วย 1 คือ 0

ในปีนี้ทาร์ซานจะได้รับการปกป้องทองของเขากับการปฏิบัติใหม่ซึ่งแต่ละเริ่มต้นที่ยอดn - 1ชิงช้าจุดสุดยอดn - 2ยังคงจุดสุดยอดn - 3ฯลฯ 0จนในที่สุดเขาปลดจุดสุดยอด

คะแนนสำหรับกิจวัตรประจำวันคือผลรวมของคะแนนสำหรับการสวิงแต่ละครั้ง (รวมถึงการลงจากหลังม้า) และคะแนนสำหรับการสวิงคือระยะทางภายในต้นไม้ระหว่างจุดเริ่มต้นและจุดสิ้นสุด ดังนั้นรูทีนของทาร์ซานบนต้นไม้มาตรฐาน 5 มีคะแนน 6:

  • แกว่งจาก4ไป3คะแนนสามคะแนน (ลง, ขึ้น, ขึ้น)
  • แกว่งจาก3ไป2จุดหนึ่งคะแนน (ลง)
  • แกว่งจาก2ไป1คะแนนหนึ่งจุด (ลง) และ
  • ลงจากหลังม้าจาก1ไป0คะแนนจุดหนึ่ง (ลดลง)

เขียนโปรแกรมหรือฟังก์ชั่นที่ได้รับเป็นจำนวนเต็มบวกคำนวณคะแนนของกิจวัตรประจำวันของทาร์ซานบนต้นไม้มาตรฐานn nตัวอย่างอินพุตและเอาต์พุต:

 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

หลักเกณฑ์และรหัสการให้คะแนนเป็นปกติสำหรับรหัสกอล์ฟ


9
ฉันไม่พบลำดับนี้ใน OEIS เป็นคำถามที่ดี
Leun Nun

8
สเป็คที่ยอดเยี่ยม!
xnor

1
@LeakyNun มันควรจะถูกเพิ่มเข้ามา มันเป็นลำดับดั้งเดิมมาก ! (แม้ไม่มี backstory)
DanTheMan

คำตอบ:


12

C, 98 97 ไบต์

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

วิธีนี้จะคำนวณระยะห่างระหว่างคะแนนแต่ละคู่ด้วยสูตรต่อไปนี้:

  • เพิ่มระยะทางจากรูทไปยังโหนด A
  • เพิ่มระยะทางจากรูทไปยังโหนด B
  • ลบ 2 * ความยาวของรูททั่วไปของ A และ B

นี่เป็นข้อได้เปรียบที่เมื่อนำไปใช้กับทุกคู่มันจะเหมือนกับ:

  • เพิ่ม 2 * ระยะทางจากรูทไปยังแต่ละโหนด
  • ลบ 2 * ความยาวของรูททั่วไปของแต่ละคู่โหนด
  • ลบระยะทางจากรูทไปที่โหนดแรก
  • ลบระยะทางจากรูทไปยังโหนดสุดท้าย

เพื่อให้ตรรกะง่ายขึ้นเราคิดว่าเราจะเปลี่ยนจากโหนด 0 ไปเป็นโหนด n-1 แทนที่จะเป็น n-1 ถึง 0 เป็นสถานะคำถาม ระยะทางจากรูตโหนดไปยังโหนด 0 เห็นได้ชัดว่า 0 (เหมือนกัน) และเราจะเห็นได้ว่าสำหรับต้นไม้ (ส่วนใหญ่) ระยะทางจากโหนดสุดท้ายถึงรากคือ 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

ซึ่งหมายความว่าเรามีกรณีพิเศษ (n <2) แต่เราสามารถอธิบายกรณีเหล่านั้นได้ง่าย

ทำให้พังถล่ม:

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
}

ขอบคุณ @feersum สำหรับบันทึก 1 ไบต์


โบนัส: ต้นไม้!

ฉันเขียนโปรแกรมที่รวดเร็วและสกปรกเพื่อดูว่าต้นไม้เหล่านี้มีลักษณะอย่างไร นี่คือผลลัพธ์บางส่วน:

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                       

มีเครื่องหมายวงเล็บบางฟุ่มเฟือยในคำสั่ง return
feersum

@feersum d'oh! พวกเขาไม่ได้ฟุ่มเฟือยเสมอ แต่จากนั้นฉันเปลี่ยนการจัดการกรณีพิเศษ ขอบคุณ!
เดฟ

3
รักการสร้างภาพ!
Edward


7

Perl, 65 59 55 54 ไบต์

รวมถึง +2 สำหรับ -ap

ทำงานกับขนาดต้นไม้บน 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

คำอธิบาย

หากคุณเขียนต้นไม้ใหม่

3
|
2   4
 \ /
  1
  |
  0

ถึงหนึ่งที่แต่ละโหนดมีชุดของบรรพบุรุษทั้งหมดและตัวเอง:

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

จากนั้นเราสามารถอธิบายเช่นโหนดทั้งหมดเส้นทางจาก 4 เป็น 3 เป็น:

  • โหนดทั้งหมดที่มี 3 แต่ไม่ใช่ 4 (ลงจาก 3)
  • โหนดทั้งหมดที่มี 4 แต่ไม่ 3 (ลงจาก 4)
  • โหนดสูงสุดที่มีทั้ง 3 และ 4 (การรวม)

จำนวนของขอบคือหนึ่งน้อยกว่าจำนวนโหนดเพื่อให้เราสามารถใช้เพื่อละเว้นจุดเชื่อมต่อดังนั้นจำนวนของขอบบนเส้นทางจาก 4 ถึง 3 คือ 3 เพราะ:

  • จำนวนโหนดที่มี 3 แต่ไม่ใช่โหนด 4: 2
  • จำนวนโหนดที่มี 4 แต่ไม่ใช่โหนด 3: 1

โปรดสังเกตว่าวิธีนี้ใช้ได้กับพา ธ ที่ไปยังเป้าหมายโดยตรงเช่นสำหรับเส้นทางจาก 3 ถึง 2 จำนวนขอบคือ 1 เพราะ:

  • จำนวนโหนดที่มี 2 แต่ไม่ใช่โหนด 3: 0
  • จำนวนโหนดที่มี 3 แต่ไม่ใช่โหนด 2: 1

จากนั้นเราสามารถหาผลรวมของชุดค่าผสมเหล่านี้ทั้งหมด

หากคุณแทนที่จะมองไปที่เพียงโหนดเช่นโหนด 2 {2,3}กับชุดบรรพบุรุษ โหนดนี้จะมีส่วนร่วมในครั้งเดียวเมื่อการประมวลผลเส้นทาง2 to 1เพราะมันมี 2 แต่ไม่ได้เป็น 1 3 to 2เนื่องจากมันมีทั้ง 2 และ 3 แต่มันจะมีส่วนร่วมเมื่อประมวลผลเส้นทาง4 to 3ตั้งแต่มันมี 3 แต่ หมายเลข 4 โดยทั่วไปแล้วตัวเลขในชุดบรรพบุรุษของโหนดจะมีส่วนหนึ่งสำหรับแต่ละเพื่อนบ้าน (หนึ่งที่ต่ำกว่าสูงกว่า) ที่ไม่ได้อยู่ในชุด ยกเว้นองค์ประกอบสูงสุด (4 ในกรณีนี้) ซึ่งสนับสนุนเฉพาะเพื่อนบ้านต่ำ 3 เนื่องจากไม่มีเส้นทาง5 to 4. Simular 0 เป็นด้านเดียว แต่เนื่องจาก 0 อยู่ที่รากของต้นไม้เสมอและมีตัวเลขทั้งหมด (เป็นการรวมขั้นสุดท้ายและเราไม่นับการรวม) ไม่มีการสนับสนุนใด ๆ จาก 0 ดังนั้นจึงง่ายที่สุดที่จะออกจากโหนด 0 ออกไปโดยสิ้นเชิง

ดังนั้นเราจึงสามารถแก้ปัญหาได้โดยดูที่บรรพบุรุษที่ตั้งค่าไว้สำหรับแต่ละโหนดนับการมีส่วนร่วมและผลรวมของโหนดทั้งหมด

เพื่อให้ง่ายต่อการประมวลผลเพื่อนบ้านฉันจะแสดงชุดบรรพบุรุษเป็นสตริงของช่องว่างและ 1 โดยที่แต่ละ 1 ที่ตำแหน่ง p แสดงว่า n-1-p เป็นบรรพบุรุษ เช่นในกรณีของเราn=5ที่ 1 ที่ตำแหน่ง 0 หมายถึง 4 เป็นบรรพบุรุษ ฉันจะออกจากช่องว่างต่อท้าย ดังนั้นการเป็นตัวแทนที่แท้จริงของต้นไม้ฉันจะสร้างคือ:

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

โปรดสังเกตว่าฉันออกจากโหนด 0 ซึ่งจะถูกแทนด้วย"11111"เพราะฉันจะไม่สนใจโหนด 0 (มันไม่เคยมีส่วนร่วม)

บรรพบุรุษที่ไม่มีเพื่อนบ้านต่ำกว่าจะถูกแทนด้วยการสิ้นสุดของลำดับที่ 1 บรรพบุรุษที่ไม่มีเพื่อนบ้านที่สูงกว่าจะถูกแทนด้วยการเริ่มต้นลำดับที่ 1 แต่เราควรเพิกเฉยต่อการเริ่มต้นลำดับที่จุดเริ่มต้นของสตริงเนื่องจากสิ่งนี้จะแสดงถึงเส้นทาง5 to 4ที่ไม่มีอยู่ ชุดนี้จะถูกจับคู่ตรงโดย /.\b/regex

การสร้างสตริงบรรพบุรุษทำโดยการประมวลผลโหนดทั้งหมดตามลำดับn-1 .. 1และมีการตั้งค่า 1 ในตำแหน่งสำหรับโหนดตัวเองและการทำ "หรือ" ลงในลูกหลาน

ด้วยโปรแกรมที่ง่ายต่อการเข้าใจ:

-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

โปรดสังเกตว่าการแทนที่/.\b/ด้วยการ/\b/แก้ปัญหารุ่นไปกลับของปัญหานี้ที่ tarzan ใช้เส้นทางด้วย0 to n-1

ตัวอย่างบางส่วนของวิธีที่สตริงบรรพบุรุษดู (ตามลำดับ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

อ๊ะขออภัยฉันไม่ทราบว่าการแก้ไขของคุณเก่าเพียงไม่กี่วินาที อย่างไรก็ตามวิธีการและคำอธิบายที่ประณีตมาก!
FryAmTheEggman

@FryAmTheEggman ไม่มีปัญหาเราเพิ่งแก้ไขปัญหาเลย์เอาต์เดียวกัน อย่างไรก็ตามใช่ฉันมีความสุขมากกับชิ้นส่วนทั้งหมดมารวมกันในโปรแกรมนี้ ตอนนี้ฉันไม่เห็นไขมันเลยที่จะถูกตัดออก ..
Ton Hospel

3

Mathematica, 113 103 102 ไบต์

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

-10 ไบต์ขอบคุณ @feersum; -1 ไบต์ขอบคุณ @MartinEnder

ต่อไปนี้เป็นไกลเร็ว ( แต่อีกต่อไปน่าเสียดายที่158 ไบต์ ):

(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]])&

Withผมเชื่อว่าคุณสามารถกำหนดสิ่งที่โดยไม่ต้องใช้ นอกจากนี้ดูเหมือนว่าทุกครั้งที่Rangeมีการใช้aเป็นอาร์กิวเมนต์เพื่อให้สามารถแยกออก
feersum

1
r=Range[a=#-1]บันทึกเป็นไบต์
Martin Ender

2

J, 37 ไบต์

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

การใช้งาน:

   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

ลองออนไลน์ได้ที่นี่


ฉันสนใจที่จะเห็นรายละเอียดของการทำงานนี้ นอกจากนี้บริการ tryj.tk ก็ดูเหมือนว่าจะเสีย ("ล้มเหลวในการอ่าน localStorage ... " และ "$ (…) .terminal ไม่ใช่ฟังก์ชั่น")
เดฟ

@Dave เว็บไซต์นั้นไม่ได้ผลสำหรับฉันด้วยบน Chrome แต่ใช้งานได้หากฉันลองใช้ IE หรือ Edge แต่ฉันแนะนำให้ติดตั้ง J ( ลิงก์ ) หากคุณสนใจ!
ไมล์

@miles Weird สำหรับฉันมันใช้ได้กับทุกเบราว์เซอร์ (FF, Chrome, IE)
Randomra

มันไม่ทำงานสำหรับฉันใช้ Chrome แต่มันหยุดทำงานไม่กี่เดือนที่ผ่านมาและเริ่มมีการตอบสนองข้อผิดพลาดที่คล้ายกับเดฟ
ไมล์

@Edward จะทำเมื่อฉันหาเวลา
randomra

1

JavaScript (ES6), 118 116 ไบต์

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

การขาดฟังก์ชั่นการตั้งค่าที่แตกต่างกันทำให้เจ็บจริงๆ แต่การเรียกใช้ซ้ำโฆษณาบางอย่างจะลดจำนวนไบต์ลงเล็กน้อย แก้ไข: บันทึก 2 ไบต์โดยลบพารามิเตอร์ที่ไม่จำเป็นออก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.