zoukankan      html  css  js  c++  java
  • POJ 3150 Cellular Automaton(矩阵乘法+二分)

    题目链接

    题意 : 给出n个数形成环形,一次转化就是将每一个数前后的d个数字的和对m取余,然后作为这个数,问进行k次转化后,数组变成什么。

    思路 :下述来自here

    首先来看一下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 。。。

     1 //3150
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <iostream>
     5 #define LL long long
     6 
     7 using namespace std ;
     8 
     9 int n,m , d , k ;
    10 LL a[1010] ,b[1010] ;
    11 void multi(LL *c,LL *d)
    12 {
    13     LL x[1010] ;
    14     for(int i = 0 ; i < n ; i++)
    15     {
    16         x[i] = 0 ;
    17         for(int j = 0 ; j < n ; j++)
    18             x[i] += c[j] * d[i >= j ? (i - j) : (n + i - j)] ;//防止是负数,形成环
    19     }
    20     for(int i = 0 ; i < n ; i++)
    21         d[i] = x[i] % m ;
    22 }
    23 int main()
    24 {
    25   while(cin >> n >> m >> d >> k ){
    26     for(int i = 0 ; i < n  ; i++)
    27         cin >> a[i] ;
    28     b[0] = 1 ;
    29     for(int i = 1 ; i <= d ; i++)
    30         b[i] = b[n - i] = 1 ;
    31     while(k)
    32     {
    33         if(k & 1)//奇数
    34             multi(b,a) ;
    35         multi(b,b) ;
    36         k >>= 1 ;
    37     }
    38     for(int i = 0 ; i < n ; i++)
    39         if(i == n-1) printf("%I64d
    ",a[i]) ;
    40         else printf("%I64d ",a[i]) ;
    41   }
    42     return 0 ;
    43 }
    View Code



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

  • 相关阅读:
    设计模式 创建型 单例模式
    设计模式 创建型 抽象工厂模式
    设计模式六大原则
    设计模式 创建型 工厂方法模式
    设计模式 创建型 简单工厂模式
    Junit TDD
    覆盖索引
    多列索引 单列索引
    JDBC PreparedStatement Statement
    常用·SQL
  • 原文地址:https://www.cnblogs.com/luyingfeng/p/3873951.html
Copyright © 2011-2022 走看看