评分Tarzan的奥林匹克葡萄摇摆常规


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

例行程序的分数是每个挥杆(包括拆卸)的分数之和,挥杆的分数是树内起点和终点之间的距离。因此,Tarzan在标准树5上的例程得分为6:

  • 从摇摆43得分3分(向下,向上,向上),
  • 从摇摆32得分(下降)一分,
  • 从摆动21得分1分(向下),并且
  • 从下来10得分为1分(下降)。

编写一个程序或函数,给定一个正整数n,该程序或函数将计算Tarzan例程在Standard Tree上的得分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中找不到此序列。好问题。
Leaky Nun

8
优秀的规格!
xnor

1
@LeakyNun它应该被添加。这是一个非常原始的序列!(即使没有背景故事)
DanTheMan

Answers:


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'哦!它们并不总是多余的,但是后来我更改了特殊情况处理。谢谢!
戴夫

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个节点的节点数

然后,我们可以总结所有这些组合。

如果您只看一个节点,例如具有祖先set的节点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,其中位置p处的每个1表示n-1-p是祖先。因此,例如,n=5在位置0 处为1 的情况下,表示4是祖先。我将保留尾随空格。因此,我将构建的树的实际表示为:

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

请注意,"11111"由于我将忽略节点0(它永远不会起作用),所以我省略了用0表示的节点0。

现在,没有下位邻居的祖先由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/解决了此问题的往返版本,其中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'8

@FryAmTheEggman没问题,我们只是在解决完全相同的布局问题。不管怎样,是的,我对程序中的各个部分如何组合感到非常满意。我目前没有看到任何脂肪被削减..
吨Hospel

3

数学,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 bytes):

(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

@Dave该网站在Chrome上也不适合我使用,但是如果我尝试使用IE或Edge可以使用,但如果您对此感兴趣,我建议您安装J(链接)!
英里

@miles Weird,对我来说,它适用于所有浏览器(FF,Chrome,IE)。
randomra

它使用Chrome确实对我有用,但几个月前它停止工作,并开始以类似的错误消息响应Dave's
英里

@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个字节。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.