Project Euler 292

Project Euler 292

题目

Pythagorean Polygons

We shall define a pythagorean polygon to be a convex polygon with the following properties:

  • there are at least three vertices,
  • no three vertices are aligned,
  • each vertex has integer coordinates,
  • each edge has integer length.

For a given integer \(n\), define \(P(n)\) as the number of distinct pythagorean polygons for which the perimeter is \(\le n\).

Pythagorean polygons should be considered distinct as long as none is a translation of another.

You are given that \(P(4)=1, P(30)=3655\) and \(P(60)=891045\).

Find \(P(120)\).

解决方案

这题可以使用动态规划来解决。令\(N=120\)

我们可以将所有凸多边形的每一条边都加上同一个方向的箭头,以此方便进行我们进行动态规划状态的设计。

首先枚举出所有模长\(\le N\)的位移向量\((x,y,d),d=\sqrt{x^2+y^2}\),存储在数组\(v\)中,并且将\(v\)中所有向量按照极角进行排序。此处假设所有位移向量\(v\)已经从小到大排好序(也就是逆时针方向)。并且,我们已经离散化好所有极角,假设一共有\(m\)种不同的极角,且第\(i\)小的极角大小为\(i\)。我们还需要注意的是,同一极角下的向量最多只能用一个,否则将会造成两条共线的边退化成同一条边,这些向量已经存储在了\(u[i]\)中。

令状态\(f(i,x,y,d)(i\ge 0,0\le d\le N)\)表示从原点\((0,0)\)起,使用了前\(i\)种极角不同的向量后,走到了位置\((x,y)\),并且走过距离为\(d\)的方案数。一开始没有进行任何位移时,就没有使用任何的位移向量,因此\(f(0,0,0,0)=1\)

本题的动态规划过程考虑使用 “我为人人” 的方法,本质上是从当前状态使用了其中一个属于\(u[i]\)的向量走到了下一步,有如下两种转移方式。

\(f(i,x,y,d)\rightarrow f(i+1,x,y,d)\),也就是说,第\(i\)种位移向量不被使用。

\(f(i,x,y,d)\rightarrow f(i+1,x+p.x,y+p.y,d+p.d)\),其中位移向量\(p\in u[i+1]\)

那么最终答案为\(\displaystyle{\sum_{d=1}^N f(m,0,0,d)}-c(v)\)。其中\(c(v)\)表示\(v\)中所有位移向量中,长度不超过\(\dfrac{N}{2}\)的个数的一半(因为我们还需要排除这种步行\(2\)步,直接掉头回到原点的情况。)

为了加快程序的运行效率,实现时进行了如下优化:

  • 只考虑满足\(x^2+y^2\le \dfrac{N^2}{4}\)中的所有点,因为走出了这个范围的点最终没有足够距离走回点\((0,0)\)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <bits/stdc++.h>
# define pi pair<int,int>
# include "tools.h"
using namespace std;
typedef long long ll;

const int N=120;

struct V{
int x,y,d;
int operator ^ (V &v) const{
return x*v.y-y*v.x;
}
bool operator < (V &v) const{
return (*this^v)>0;
}
};

const int O=N*2;
int ok[O*2+4][O*2+4];
ll f[2][O*2+4][O*2+4][N+4];
int main(){
vector<V>v;
vector<pi>pt_ls;
for(int x=-N;x<=N;x++)
for(int y=-N;y<=N;y++){
int d2=x*x+y*y;
if(d2<=N*N/4) pt_ls.emplace_back(O+x,O+y),ok[O+x][O+y]=1;
if(d2<=N*N&&is_square(d2)&&d2!=0){
int d=int_square(d2);
v.push_back(V{x,y,d});
}
}
sort(v.begin(),v.end());
while (v.size()>1&&(v.front()^v.back())==0){
v.push_back(v.front());v.erase(v.begin());
}
int m=v.size(),p=0;
f[0][O][O][0]=1;
for(int i=0,j;i<m;i=j,p^=1){
for(j=i;j<m&&(v[i]^v[j])==0;++j);
for(auto &[px,py]:pt_ls) {
for (int d=0;d<=N;d++) {
f[p^1][px][py][d]=f[p][px][py][d];
}
}
for(auto &[px,py]:pt_ls) {
for (int d=0;d<=N;d++) {
if(!f[p][px][py][d]) continue;
for(int k=i;k<j;k++){
int nx=px+v[k].x,ny=py+v[k].y;
if(ok[nx][ny]&&v[k].d+d<=N)
f[p^1][nx][ny][v[k].d+d]+=f[p][px][py][d];
}
}
}
}
ll ans=0,t=0;
for(int d=1;d<=N;d++)
ans+=f[p][O][O][d];
for(V &p:v)
if(p.d*2<=N) ++t;
ans-=t/2;
printf("%lld\n",ans);
}