0292 勾股多边形
* * * *
拉格朗日计划
* * * *
勾股多边形

定义满足以下性质的凸多边形为勾股多边形:
  • 有至少三个顶点,且任意三个顶点不共线。

  • 每个顶点坐标均为整数,每条边长度也均为整数。
给定整数n,记$P(n)$为周长不超过n的不同勾股多边形的数量(只相差平移的两个勾股多边形视为相同)。

已知$P(4) = 1, P(30) = 3655, P(60) = 891045$。求$P(120)$。

本题难度:



解答

考察所有坐标为整数、绝对值不超过120,且长度不超过60(否则无法回到原点)的向量作为各边的候选,将这些向量按极角排序。

考虑一个从原点出发,按逆时针方向沿着各边前进的质点,用$f(x,y,d)$表示到达点$(x,y)$且总行进距离为d的走法总数。

枚举极角以及每个极角所对应的向量,就可以递推生成上述函数。最后汇总$\sum_df(0,0,d)$,再扣除向量总数的一半(同向量原路返回的情况)即得结果$3600060866$。

注:以下为Python 3代码,因math.isqrt为Python 3新增函数。此外,代码中打印了进度信息。

import math

target=120

f=[{} for i in range(2)]

v={}
for x in range(-target,target+1):
    for y in range(-target,target+1):
        d2=x*x+y*y
        d=math.isqrt(d2)
        if target*target>=4*d2>0 and d*d==d2:
            t=math.atan2(y,x)
            if t < 0:t+=2*math.pi
            if t in v:
                v[t].append((x,y,d))
            else:
                v[t]=[(x,y,d)]
t=sorted(v.keys())

m,p=len(t),0
f[0][(0,0,0)]=1

i=0
while i < m:
    f[1-p]=f[p].copy()

    for x,y,d in f[p]:
        for dx,dy,dd in v[t[i]]:
            x2,y2=x+dx,y+dy
            if dd+d<=target:
                f[1-p][(x2,y2,dd+d)]=f[1-p].get((x2,y2,dd+d),0)+f[p][(x,y,d)]
    i+=1
    p=1-p
    print(i,"out of",m,"cases completed")

print(sum(f[p].get((0,0,d),0) for d in range(1,target+1))-sum(1 for theta in v for x,y,d in v[theta])//2)