NxM网格上的平行四边形数量

时间:2014-01-27 18:13:37

标签: c++ algorithm mathematical-lattices

当给定网格大小N x M时,我必须解决一个问题,我必须找到“可以放入其中”的平行四边形的数量,这样它们每个坐标都是一个整数。

这是我的代码:

/*
      ~Keep It Simple!~
*/

#include<fstream>

#define MaxN 2005

int N,M;
long long Paras[MaxN][MaxN]; // Number of parallelograms of Height i and Width j
long long Rects; // Final Number of Parallelograms

int cmmdc(int a,int b)
{
while(b)
{
    int aux = b;
    b = a -(( a/b ) * b);
    a = aux;
}

return a;
}

int main()
{
freopen("paralelograme.in","r",stdin);
freopen("paralelograme.out","w",stdout);

scanf("%d%d",&N,&M);

for(int i=2; i<=N+1; i++)
    for(int j=2; j<=M+1; j++)
    {
        if(!Paras[i][j])
          Paras[i][j] = Paras[j][i] = 1LL*(i-2)*(j-2) + i*j - cmmdc(i-1,j-1) -2; // number of parallelograms with all edges on the grid + number of parallelograms with only 2 edges on the grid.
        Rects += 1LL*(M-j+2)*(N-i+2) * Paras[j][i]; // each parallelogram can be moved in (M-j+2)(N-i+2) places.
    }

printf("%lld", Rects);
}

示例:对于2x2网格,我们有22个可能的平行四边形。

我的算法工作正常,但我需要让它快一点。我想知道怎么可能。

P.S。我听说我应该预先处理最大公约数并将其保存在一个数组中,这会将运行时间减少到O(n * m),但我不知道如何在不使用cmmdc的情况下执行此操作(最大公约数)。

2 个答案:

答案 0 :(得分:0)

确保N不小于M:

if( N < M ){ swap( N, M ); }

利用循环中的对称性,您只需要将j从2运行到i:

for(int j=2; j<=min( i, M+1); j++)

你不需要额外的数组Paras,放弃它。而是使用临时变量。

long long temparas = 1LL*(i-2)*(j-2) + i*j - cmmdc(i-1,j-1) -2;
long long t1 = temparas * (M-j+2)*(N-i+2);
Rects += t1;
// check if the inverse case i <-> j must be considered
if( i != j && i <= M+1 ) // j <= N+1 is always true because of j <= i <= N+1
    Rects += t1;

使用余数运算符替换此行:b = a -(( a/b ) * b);

b = a % b;

缓存cmmdc结果可能是可能的,你可以使用筛选算法初始化数组:创建一个由a和b索引的二维数组,在每个位置放置“2”,其中a和b是2的倍数,然后在每个位置放一个“3”,其中a和b是3的倍数,依此类推,大致如下:

int gcd_cache[N][N];

void init_cache(){
    for (int u = 1; u < N; ++u){
        for (int i = u; i < N; i+=u ) for (int k = u; k < N ; k+=u ){
            gcd_cache[i][k] = u;
        }
    }
}

不确定它是否有帮助。

答案 1 :(得分:0)

代码中的第一条评论说“保持简单”,因此,鉴于此,为什么不尝试以数学方式解决问题并打印结果。

如果您从网格中选择两条长度为N的线,您将通过以下方式找到平行四边形的数量:

  • 在两行中选择彼此相邻的两个点:(N-1)^2 这样做的方法,因为你可以将这两点放在N-1上 每条线上的位置。
  • 在两行中选择两个点之间有一个空格:有(N-2)^2种方法。
  • 选择两个点,它们之间有两个,三个和最多N-2个空格。
  • 得到的组合数量为(N-1)^2+(N-2)^2+(N-3)^2+...+1
  • 通过求解和,我们得到公式:1/6*N*(2*N^2-3*N+1)。点击WolframAlpha进行验证。

既然你有两条线的解决方案,你只需要将它乘以M的2阶combinations的数量,即M!/(2*(M-2)!)

因此,整个公式为:1/12*N*(2*N^2-3*N+1)*M!/(M-2)!,其中!标记表示factorial^表示幂运算符(请注意,相同的符号是不是C ++中的幂运算符,而是按位XOR运算符。)

此计算需要较少的迭代遍历矩阵的操作。