ターザンのオリンピックつるスイングル​​ーチンを記録する


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までのスイングが33点(ダウン、アップ、アップ)を獲得し、
  • から3にスイングして21ポイント(ダウン)を獲得し、
  • から2までのスイングが11ポイント(ダウン)を獲得し、
  • から降車し101ポイント(下)を獲得します。

正の整数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でこのシーケンスを見つけることができません。いい質問です。
リーキー修道女

8
優れたスペック!
xnor

1
@LeakyNunただし、追加する必要があります。それは非常にオリジナルのシーケンスです!(バックストーリーなしでも)
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 *各ノードペアの共通ルートの長さを引く
  • ルートから最初のノードまでの距離を引きます
  • ルートから最後のノードまでの距離を引きます

ロジックをより簡単にするために、質問が示すようにn-1から0ではなく、ノード0からノードn-1に行くと仮定します。ルートノードからノード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
}

1バイト保存してくれてありがとう@feersum


ボーナス:木!

これらのツリーがどのように見えるかを確認するために、手っ取り早くプログラムを作成しました。結果の一部を次に示します。

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
視覚化が大好きです!
エドワード

7

Python 2、85バイト

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バイト

+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の両方を含む最上位ノード(結合)

エッジの数はノードの数よりも1つ少ないため、それを使用して結合ポイントを無視できるため、4から3までのパス上のエッジの数は3です。

  • 3ではなく4を含むノードの数:2ノード
  • 4を含むが3を含まないノードの数:1ノード

これは、ターゲットに直接到達するパスに対しても機能することに注意してください。たとえば、3から2のパスの場合、エッジの数は1です。

  • 2を含むが3を含まないノードの数:0ノード
  • 3を含むが2を含まないノードの数:1ノード

その後、これらすべての組み合わせを合計できます。

代わりに先祖が設定されたノード2などのノードだけを見る場合{2,3}。このノードは2 to 1、1ではなく2を含むため、パスを処理するときに1回寄与します3 to 2。2と3の両方があるため、パスには何も寄与しませんが、4 to 33いいえ4.一般に、ノードの祖先セット内の番号は、セット内にない各隣接ノード(上位または下位)に1つずつ寄与します。パスが存在しないため、下位3にのみ寄与する最大要素(この場合は4)を除く5 to 4。Simular 0は片側ですが、0は常にツリーのルートにあり、すべての数値を含みます(最終的な結合であり、結合はカウントしません)。完全にアウト。

そのため、各ノードの祖先セットを調べ、貢献度を数え、すべてのノードで合計することで問題を解決することもできます。

ネイバーを簡単に処理するために、祖先セットをスペースと1の文字列として表します。ここで、位置pの各1は、n-1-pが祖先であることを表します。たとえば、n=5位置0の1の場合、4が祖先であることを示します。末尾のスペースは省略します。したがって、構築するツリーの実際の表現は次のとおりです。

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

ノード0 "11111"を無視したことに注意してください(ノードは貢献しません)。

下位ネイバーのない祖先は、1のシーケンスの終わりで表されるようになりました。上位のネイバーを持たない祖先は、1のシーケンスの先頭で表されるようになりましたが、5 to 4存在しないパスを表すため、文字列の先頭でシーケンスの先頭を無視する必要があります。この組み合わせは、正規表現と完全に一致し/.\b/ます。

祖先文字列の構築は、すべてのノードを順番に処理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/、この問題の往復バージョンが解決され、ターザンもパスを取得します。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問題ありません。まったく同じレイアウトの問題を修正しただけです。とにかく、ええ、私はこのプログラムですべてのピースがどのようにまとめられたかにとても満足しています。私は現在、カットされる脂肪を見ていません。
トンHospel

3

Mathematica、113 103 102バイト

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

@feersumのおかげで-10バイト。@MartinEnderのおかげで-1バイト

次の方がはるかに高速です(ただし、残念ながら、 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]バイトを保存します。
マーティンエンダー

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は、時間があれば見つけます。
ランダラ

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.