• 矩阵十题(7)


    Matrix67原文:经典题目7 VOJ1067

    我们可以用上面的方法二分求出任何一个线性递推式的第n项,其对应矩阵的构造方法为:在右上角的(n-1)*(n-1)的小矩阵中的主对角线上填1,矩阵 第n行填对应的系数,其它地方都填0。例如,我们可以用下面的矩阵乘法来二分计算f(n) = 4f(n-1) - 3f(n-2) + 2f(n-4)的第k项:

    利用矩阵乘法求解线性递推关系的题目我能编出一卡车来。这里给出的例题是系数全为1的情况。

    下面是题解

    vijos  1067   Warcraft III 守望者的烦恼

    题目链接:https://vijos.org/p/1067

    题目大意:有n个监狱排成一列,每次最多可以往前走k个监狱,问走到第n个监狱有多少种方案,结果mod 7777777

    1<=k<=10, 1<=n<=2^31-1

    这道题显然是一道DP,但是数据范围太大了,所以我们要去优化整个DP。

    设f[n]为监狱长度为n时的方案数,令f[0]=1

    我们从起点出发,每次最多可以走k步,求我们到达点n的方案数f[n],可以分成刚开始走1步,2步,3步....k步的情况,如果刚开始走的是1步,则问题变成走了1步之后,我们从起点出发到达点n-1的方案数f[n-1],

    如果刚开始走的是2步,则问题变成走了2步之后,我们从起点出发到达点n-2的方案数f[n-2],

    以此类推.....

    如果刚开始走的是k步,则问题变成走了k步之后,我们从起点出发到达点n-k的方案数f[n-k],

    我们可以发现,这就变成了一个递推的问题了,然后只要我们把走1步,走2步.....走k步后对应的方案数加起来就变成我们要求的f[n]了,

    即状态转移方程为:f[n]=f[n-1]+f[n-2]+....f[n-k]

    f[0]=1

    当k=1,   f[1]=1;

                f[2]=f[1]=1;

                f[3]=f[2]=1;

                f[4]=f[3]=1;

    当k=2,   f[1]=1;

                 f[2]=f[1]+f[0]=2;

                 f[3]=f[2]+f[1]=3;

                 f[4]=f[3]+f[2]=5;

    当k=3,    f[1]=1;

                 f[2]=f[1]+f[0]=2;

                 f[3]=f[2]+f[1]+f[0]=4;

                 f[4]=f[3]+f[2]+f[1]=7;

    我们手算验证一下发现符合递推式,故推出来的状态转移方程是正确的。

    其实任何一个递推式都可以用矩阵乘法来进行优化,我们可以用二分求出任何一个线性递推式的第n项,

    其对应矩阵的构造方法为:(和Matrix67的方法有点小不同,此处是Matrix67方法构造的矩阵的转置)

    在左下角的(n-1)*(n-1)的小矩阵中的主对角线上填1,矩阵第n列填对应的系数,其它地方都填0。

    例如,我们可以用下面的矩阵乘法来二分计算f(n) = 4f(n-1) - 3f(n-2) + 2f(n-4)的第k项:

    [ [ 0  1  0  0]          [f(k-4)]       [f(k-3)]

      [ 0  0  1  0]     *   [f(k-3)]   =  [f(k-2)]

      [ 0  0  0  1]          [f(k-2)]       [f(k-1)]

      [ 2  0 -3  4] ]       [f(k-1)]       [f( k )]

    观察一下DP转移方程:F[i]=F[i-1]+F[i-2]+...+F[i-k];注意到这是一个形如f[i]=a1*f[i-1]+a2*f[i-2]+...+ak[i-k];的方程,立刻就反应到可以使用矩阵乘法进行优化。

    因此构造一个k*k的矩阵A然后求n-k次幂A^(n-k),然后用递推式构造前k项,然后用之与前面求出的矩阵A^(n-k)相乘得到f[n]即可。

    我的构造方法,以k=2为例

    [f(1),f(2)] *  [ [ 0 1 ]    ^(n-k)   =  [f(n-1),f(n)] 

                         [ 1 1 ] ]

    代码如下:

     1 #include<stdio.h>
     2 #include<string.h>
     3 #define N 11
     4 #define M 7777777
     5 struct Matrix
     6 {
     7     __int64 a[N][N];
     8 }res,tmp,origin,A,ans;
     9 int n,m,f[N];
    10 Matrix mul(Matrix x,Matrix y)
    11 {
    12     int i,j,k;
    13     memset(tmp.a,0,sizeof(tmp.a));
    14     for(i=1;i<=n;i++)
    15         for(j=1;j<=n;j++)
    16             for(k=1;k<=n;k++)
    17             {
    18                 tmp.a[i][j]+=(x.a[i][k]*y.a[k][j])%M;
    19                 tmp.a[i][j]%=M;
    20             }
    21     return tmp;
    22 }
    23 void quickpow(int k)  //矩阵快速幂
    24 {
    25     int i;
    26     memset(res.a,0,sizeof(res.a));
    27     for(i=1;i<=n;i++)
    28         res.a[i][i]=1;
    29     while(k)
    30     {
    31         if(k&1)
    32             res=mul(res,A);
    33         A=mul(A,A);
    34         k>>=1;
    35     }
    36 }
    37 int main()
    38 {
    39     int k,i,j;
    40     while(scanf("%d%d",&k,&m)!=EOF)
    41     {
    42         memset(f,0,sizeof(f));
    43         f[0]=1;
    44         for(i=1;i<=k;i++)      //构造前k项
    45             for(j=0;j<i;j++)
    46                 f[i]+=f[j];
    47         memset(A.a,0,sizeof(A.a));
    48         for(i=2;i<=k;i++)      //构造矩阵A
    49             A.a[i][i-1]=1;
    50         for(i=1;i<=k;i++)
    51             A.a[i][k]=1;
    52         n=k;
    53         quickpow(m-k);   //A^(n-k)
    54         memset(origin.a,0,sizeof(origin.a));
    55         for(i=1;i<=k;i++)     //前k项的矩阵[f(1),f(2),f(3)....f(k)]
    56             origin.a[1][i]=f[i];
    57         ans=mul(origin,res);      //[f(1),f(2),f(3)....f(k)] * A^(n-k)
    58         printf("%d\n",ans.a[1][n]%M);
    59     }
    60     return 0;
    61 }
    View Code

    hdu   1757    A Simple Math Problem

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1757

    If x < 10   ,则  f(x) = x.
    If x >= 10 ,则  f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10);

     给出k,m和a0~a9,求f(k)%m,  k<2*10^9 , m < 10^5

    这是一个递推式,故可以用矩阵乘法来求

    根据上面讲的方法构建矩阵A,然后二分求A^(k-10),然后用前k项的矩阵与之相乘即可得到f[n]

    代码如下:

     1 #include<stdio.h>
     2 #include<string.h>
     3 #define N 15
     4 struct Matrix
     5 {
     6     __int64 a[N][N];
     7 }ans,tmp,res,origin,A;
     8 int n,m,f[N],a[N];
     9 Matrix mul(Matrix x,Matrix y)
    10 {
    11     int i,j,k;
    12     memset(tmp.a,0,sizeof(tmp.a));
    13     for(i=1;i<=n;i++)
    14         for(j=1;j<=n;j++)
    15             for(k=1;k<=n;k++)
    16             {
    17                 tmp.a[i][j]+=(x.a[i][k]*y.a[k][j])%m;
    18                 tmp.a[i][j]%=m;
    19             }
    20     return tmp;
    21 }
    22 Matrix quickpow(int k)  //矩阵快速幂
    23 {
    24     int i;
    25     memset(res.a,0,sizeof(res.a));
    26     for(i=1;i<=n;i++)
    27         res.a[i][i]=1;
    28     while(k)
    29     {
    30         if(k&1)
    31             res=mul(res,A);
    32         A=mul(A,A);
    33         k>>=1;
    34     }
    35     return res;
    36 }
    37 int main()
    38 {
    39     int k,i;
    40     while(scanf("%d%d",&k,&m)!=EOF)
    41     {
    42         n=10;
    43         for(i=0;i<n;i++)
    44             scanf("%d",&a[i]);
    45         f[n]=0;
    46         for(i=0;i<n;i++)   //求前10项
    47             f[i]=i;
    48         for(i=0;i<n;i++)
    49             f[n]+=a[i]*f[n-1-i];
    50         if(k<=n)
    51             printf("%d\n",f[k]);
    52         else
    53         {
    54             memset(A.a,0,sizeof(A.a));
    55             for(i=2;i<=n;i++)    //构造矩阵A
    56                 A.a[i][i-1]=1;
    57             for(i=1;i<=n;i++)
    58                 A.a[i][n]=a[n-i];
    59             res=quickpow(k-10);   //A^(k-10)
    60             memset(origin.a,0,sizeof(origin.a));
    61             for(i=1;i<=n;i++)   //前10项的矩阵[f(1),f(2),f(3)....f(10)]
    62                 origin.a[1][i]=f[i];
    63             ans=mul(origin,res);    //[f(1),f(2),f(3)....f(10)] * A^(n-10)
    64             printf("%d\n",ans.a[1][n]%m);
    65         }
    66     }
    67     return 0;
    68 }
    View Code
  • 相关阅读:
    vue跨域代理配置
    vue中引入jquery
    vue中使用特殊字体
    vue中使用mockjs
    vue中使用动态echart图表
    解决win10休眠后无法唤醒
    nvm-windows的安装配置
    黑苹果快捷键
    python基础知识
    如何高效的学习python
  • 原文地址:https://www.cnblogs.com/frog112111/p/3092012.html
Copyright © 2020-2023  润新知