[矩阵快速幂/矩阵快速幂优化DP]Exercise Week14 C+D+E

目录

  • C.[矩阵快速幂求线性递推]考验
    • 题意
    • 样例
      • 样例输入:
      • 样例输出:
    • 思路
    • 总结
    • 代码
  • D.[矩阵快速幂优化DP]染砖
    • 题意
    • 样例
      • 样例输入:
      • 样例输出:
    • 思路
    • 总结
    • 代码
  • E.[矩阵快速幂优化DP]度假
    • 题意
    • 样例
      • 样例输入:
      • 样例输出:
    • 思路
    • 总结
    • 代码

C.[矩阵快速幂求线性递推]考验

题意

这一天,Q老师 为了增强大家对于斐波那契数列的理解,决定在斐波那契的基础上创建一个新的数列 f(x) 来考一考大家。数列 f(x) 定义如下:
当 x < 10 时,f(x) = x;
当 x ≥ 10 时,f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10),ai 只能为 0 或 1。
Q老师 将给定 a0~a9,以及两个正整数 k m,询问 f(k) % m 的数值大小。

样例

样例输入:

输出文件包含多组测试用例,每组测试用例格式如下:
第一行给定两个正整数 k m。(k < 2e9, m < 1e5)
第二行给定十个整数,分别表示 a0~a9。

10 9999
1 1 1 1 1 1 1 1 1 1
20 500
1 0 1 0 1 0 1 0 1 0

样例输出:

对于每一组测试用例输出一行,表示 f(k) % m 的数值大小。

45
104

思路

1.这是一道矩阵快速幂的模板题,对于矩阵快速幂,我们有以下两部分任务要做:
①.将矩阵封装成Matrix结构体
成员:int x[N][N] 构造函数:memset(x,0,sizeof(x))
函数:重载运算符 ‘*’ 具体方法参考线性代数中矩阵的乘法 时间复杂度O(N^3)

②.写矩阵快速幂函数
当我们将矩阵封装成结构体后 由于我们已经重载了乘号 故函数内容和普通的快速幂几乎完全一致,唯一一点在于初始时需要将矩阵初始化为一个单位矩阵

2.完成以上代码的编写后,我们只需要根据递推关系式,来构造如何利用矩阵快速幂求解本题即可
[矩阵快速幂/矩阵快速幂优化DP]Exercise Week14 C+D+E_第1张图片
构造的矩阵A如图所示,故我们利用矩阵快速幂计算出Ak-9,然后再用其第一行分别与9 8···0相乘并求和即可


总结

矩阵快速幂的应用的模板题
需要记住的是 要进行两步工作,一是将矩阵封装成结构体并重载乘号 二是矩阵快速幂的函数(与一般快速幂相同)


代码

#include
#include
#include
#include
#include
#include
#define N 10
using namespace std;
int k,m;
struct Matrix
{
    int x[N][N];
    Matrix operator*(const Matrix & t) const{
        Matrix tmp;
        for(int i=0;i<N;i++)
            for(int j=0;j<N;j++)
            {
                tmp.x[i][j]=0;
                for(int k=0;k<N;k++)
                {
                    tmp.x[i][j]+=x[i][k]*t.x[k][j] % m;
                    tmp.x[i][j]%=m;
                }
            }
        return tmp;
    }

    Matrix () {memset(x,0,sizeof(x));}
    Matrix (const Matrix & t) {memcpy(x,t.x,sizeof(x));}
};

Matrix quick_pow(Matrix a,int x)
{
    Matrix tmp;//先�始化
    for(int i=0;i<N;i++)    tmp.x[i][i]=1;
    while(x)
    {
        if(x&1) tmp=tmp*a;
        a=a*a;
        x>>=1;
    }
    return tmp;
}
int main()
{
    while(cin>>k>>m)
    {
       Matrix A;
       for(int i=0;i<=9;i++) cin>>A.x[0][i];
       for(int i=1;i<=9;i++)    A.x[i][i-1]=1;
       A=quick_pow(A,k-9);
       int cur=0;
       for(int i=0;i<N;i++)
            cur=(cur+A.x[0][i]*(9-i))%m;
        cout<<cur<<endl;
    }
    system("pause");
    return 0;
}

D.[矩阵快速幂优化DP]染砖

题意

有 N 块砖排成一排染色,每一块砖需要涂上红、蓝、绿、黄这 4 种颜色中的其中 1 种。且当这 N 块砖中红色和绿色的块数均为偶数时,染色效果最佳。
为了使工作效率更高,想要知道一共有多少种方案可以使染色效果最佳

样例

样例输入:

第一行为 T,代表数据组数。(1 ≤ T ≤ 100)

接下来 T 行每行包括一个数字 N,代表有 N 块砖。(1 ≤ N ≤ 1e9)

2
1
2

样例输出:

输出满足条件的方案数,答案模 10007。

2
6

思路

1.首先思考普通的DP思路
我们只需关心红、绿两颜色的奇偶性,故可以考虑如下状态:
A[i]表示[1,i]块砖中 红颜色和绿颜色都是偶数时的方案数
B[i]表示[1,i]块砖中 红颜色和绿颜色都是奇数时的方案数
C[i]表示[1,i]块砖中 红颜色和绿颜色一个是偶数、一个是奇数的方案数
则A[1]=2 B[1]=0 C[1]=2

A[i]=2*A[i-1]+C[i-1]
B[i]=2*B[i-1]+C[i-1]
C[i]=2*A[i-1]+2*B[i-1]+2*C[i-1]

2.有了状态转移方程后,考虑到方程为线性递推的形式 故构造矩阵来借助矩阵快速幂优化
[矩阵快速幂/矩阵快速幂优化DP]Exercise Week14 C+D+E_第2张图片
故我们利用矩阵快速幂求出En-1后 再用其第一行分别于a1,b1,c1相乘并求和 即可得到答案


总结

矩阵快速幂能用来优化DP的两个条件为:
①状态转移方程是线性递推
②转移次数很多(n很大)


代码

#include
#include
#include
#include
#include
#include
#define N 3
#define P 10007
using namespace std;
int T,n;
int a1,b1,c1;
struct Matrix
{
    int x[N][N];
    Matrix operator*(const Matrix & t) const{
        Matrix tmp;
        for(int i=0;i<N;i++)
            for(int j=0;j<N;j++)
            {
                tmp.x[i][j]=0;
                for(int k=0;k<N;k++)
                {
                    tmp.x[i][j]+=x[i][k]*t.x[k][j] % P;
                    tmp.x[i][j]%=P;
                }
            }
        return tmp;
    }

    Matrix () {memset(x,0,sizeof(x));}
    Matrix (const Matrix & t) {memcpy(x,t.x,sizeof(x));}
};

Matrix quick_pow(Matrix a,int x)
{
    Matrix tmp;//先�始化
    for(int i=0;i<N;i++)    tmp.x[i][i]=1;
    while(x)
    {
        if(x&1) tmp=tmp*a;
        a=a*a;
        x>>=1;
    }
    return tmp;
}
int main()
{
    cin>>T;
    a1=2;
    b1=0;
    c1=2;
    while(T--)
    {
        cin>>n;
        Matrix A;
        A.x[0][0]=2;A.x[0][2]=1;
        A.x[1][1]=2;A.x[1][2]=1;
        A.x[2][0]=2;A.x[2][1]=2;A.x[2][2]=2;
        A=quick_pow(A,n-1);
        int ans=0;
        ans=(ans+A.x[0][0]*a1) % P;
        ans=(ans+A.x[0][1]*b1) % P;
        ans=(ans+A.x[0][2]*c1) % P;
        cout<<ans<<endl;
    }
    system("pause");
    return 0;
}

E.[矩阵快速幂优化DP]度假

题意

忙碌了一个学期的 Q老师 决定奖励自己 N 天假期。

假期中不同的穿衣方式会有不同的快乐值。

已知 Q老师 一共有 M 件衬衫,且如果昨天穿的是衬衫 A,今天穿的是衬衫 B,则 Q老师 今天可以获得 f[A][B] 快乐值。

在 N 天假期结束后,Q老师 最多可以获得多少快乐值?

样例

样例输入:

输入文件包含多组测试样例,每组测试样例格式描述如下:

第一行给出两个整数 N M,分别代表假期长度与 Q老师 的衬衫总数。(2 ≤ N ≤ 100000, 1 ≤ M ≤ 100)

接下来 M 行,每行给出 M 个整数,其中第 i 行的第 j 个整数,表示 f[i][j]。(1 ≤ f[i][j] ≤ 1000000)

测试样例组数不会超过 10。

3 2
0 1
1 0
4 3
1 2 3
1 2 3
1 2 3

样例输出:

每组测试样例输出一行,表示 Q老师 可以获得的最大快乐值。

2
9

思路

1.首先思考一般的DP时的状态和转移方程
f[i][j]可以表示第i天时穿第j件衣服可获得的最大的快乐值
则转移方程:
f[i][j]=max(f[i-1][k]+H[k][j]),1<=k<=m
这个方程与矩阵乘法计算C[i][j]时的方程很相似

2.考虑如何用矩阵快速幂来优化DP
①可以在重载乘号时用取max代替求和 用加法代替乘法 即可用矩阵乘法求解
②构造的矩阵如下所示
[矩阵快速幂/矩阵快速幂优化DP]Exercise Week14 C+D+E_第3张图片
故我们只需计算出H的逆的n-2次方 再对对应的每一行以及f[2][i]中的对应项进行求和并取max 即可得到f[n][1]~f[n][m] 答案就是max(f[n][i])
f[2][j]=max(H[i][j]) 1<=i<=n


总结

矩阵快速幂可用于优化DP转移方程中包含max,min,+,-,*,/的递推式


代码

#include
#include
#include
#include
#include
#include
#define N 110
using namespace std;
int t,n;
long long a[110][110],ans[110],ans2[110];
struct Matrix
{
    long long x[N][N];
    Matrix operator*(const Matrix & t) const{
        Matrix tmp;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            {
                tmp.x[i][j]=0;
                for(int k=0;k<n;k++)
                {
                    tmp.x[i][j]=max(x[i][k]+t.x[k][j],tmp.x[i][j]) ;
                }
            }
        return tmp;
    }

    Matrix () {memset(x,0,sizeof(x));}
    Matrix (const Matrix & t) {memcpy(x,t.x,sizeof(x));}
};

Matrix quick_pow(Matrix a,int x)
{
    Matrix tmp;//先初始化
    memset(tmp.x,0,sizeof(tmp.x));
    while(x)
    {
        if(x & 1 ) tmp=tmp*a;
        a=a*a;
        x>>=1;
    }
    return tmp;
}
int main()
{
    while(cin>>t>>n)
    {
        Matrix A;
        memset(ans,0,sizeof(ans));
        memset(ans2,0,sizeof(ans2));
        for(int i=0;i<n;i++)   
            for(int j=0;j<n;j++)    {cin>>a[i][j];A.x[j][i]=a[i][j];}
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)//ans2[j]表示第二天穿衣服j所得到的的最大快乐值
                ans2[j]=max(ans2[j],a[i][j]);
        A=quick_pow(A,t-2);
        
         for(int i=0;i<n;i++)
            for(int k=0;k<n;k++)
                ans[i]=max(ans[i],A.x[i][k]+ans2[k]);

        long long  maxl=0;
        for(int i=0;i<n;i++)    maxl=max(maxl,ans[i]);
        cout<<maxl<<endl;
    }
    system("pause");
    return 0;
}

你可能感兴趣的:(csp)