POJ 3150 Cellular Automaton 矩阵乘法+二分

POJ 3150 Cellular Automaton 矩阵乘法+二分

这题对我来说太难啦,看了报告半天才弄明白是咋回事。
高手们的解题报告相当飘逸。我来写一个造福菜鸟的。

首先来看一下Sample里的第一组数据。
1 2 2 1 2
经过一次变换之后就成了
5 5 5 5 4
它的原理就是
a0 a1 a2 a3 a4
->
(a4+a0+a1) (a0+a1+a2) (a1+a2+a3) (a2+a3+a4) (a3+a4+a0)

如果用矩阵相乘来描述,那就可以表述为1xN和NxN的矩阵相乘,结果仍为1xN矩阵
a = 1 2 2 1 2
b =
1 1 0 0 1
1 1 1 0 0
0 1 1 1 0
0 0 1 1 1
1 0 0 1 1
a * b = 5 5 5 5 4
所以最终结果就是:a * (b^k)

线性代数不合格的同鞋表示压力很大。。

对一个NxN矩阵求k次方,而且这个k很大,N也不小,怎么办?
所以有高手观察到了,这个矩阵长得有点特殊,可以找到一些规律:
b^1 =
[1, 1, 0, 0, 1]
[1, 1, 1, 0, 0]
[0, 1, 1, 1, 0]
[0, 0, 1, 1, 1]
[1, 0, 0, 1, 1]
b^2 =
[3, 2, 1, 1, 2]
[2, 3, 2, 1, 1]
[1, 2, 3, 2, 1]
[1, 1, 2, 3, 2]
[2, 1, 1, 2, 3]
b^3 =
[7, 6, 4, 4, 6]
[6, 7, 6, 4, 4]
[4, 6, 7, 6, 4]
[4, 4, 6, 7, 6]
[6, 4, 4, 6, 7]
b^4 =
[19, 17, 14, 14, 17]
[17, 19, 17, 14, 14]
[14, 17, 19, 17, 14]
[14, 14, 17, 19, 17]
[17, 14, 14, 17, 19]

发现神马没有。就是无论是b的几次幂,都符合A[i][j] = A[i-1][j-1]
高手说是这样推倒出来地:
““”
利用矩阵A,B具有a[i][j]=A[i-1][j-1],B[i][j]=B[i-1][j-1](i-1<0则表示i-1+n,j-1<0则表示j-1+n)
我们可以得出矩阵C=a*b也具有这个性质
C[i][j]=sum(A[i][t]*B[t][j])=sum(A[i-1][t-1],B[t-1][j-1])=sum(A[i-1][t],B[t][j-1])=C[i-1][j-1]
“”“

这样就可以开一个N大小的数组来存放每次计算的结果了。而没必要用NxN。
N的问题解决了,但是k还是很大,怎么办?

这时候可以用二分法来求b^k
b^k = b^1 * b^4 * b^16 。。。

计算过程中,必定会出现数字大于M的情况。
切记 x*y = (x%M)*(y%M)

最后,经过多次优化,这题的代码居然被高手写成了如下的一小坨,实在是。。给力哇

#include < iostream >
using   namespace  std;
int  n,m,d,k;
void  mul( long   long  a[], long   long  b[])
{
      
int  i,j;
      
long   long  c[ 501 ];
      
for (i = 0 ;i < n; ++ i) for (c[i] = j = 0 ;j < n; ++ j)c[i] += a[j] * b[i >= j ? (i - j):(n + i - j)];
      
for (i = 0 ;i < n;b[i] = c[i ++ ] % m);                     
}
long   long  init[ 501 ],tmp[ 501 ];
int  main()
{
    
int  i,j;
    scanf(
" %d%d%d%d " , & n, & m, & d, & k);
    
for (i = 0 ;i < n; ++ i)scanf( " %I64d " , & init[i]);
    
for (tmp[ 0 ] = i = 1 ;i <= d; ++ i)tmp[i] = tmp[n - i] = 1 ;
    
while (k)
    {
            
if (k & 1 )mul(tmp,init);
            mul(tmp,tmp);
            k
>>= 1 ;     
    }
    
for (i = 0 ;i < n; ++ i) if (i)printf( "  %I64d " ,init[i]); else  printf( " %I64d " ,init[i]);
    printf(
" \n " );
    
return   0 ;
}




你可能感兴趣的:(POJ 3150 Cellular Automaton 矩阵乘法+二分)