这一天,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.完成以上代码的编写后,我们只需要根据递推关系式,来构造如何利用矩阵快速幂求解本题即可
构造的矩阵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;
}
有 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.有了状态转移方程后,考虑到方程为线性递推的形式 故构造矩阵来借助矩阵快速幂优化
故我们利用矩阵快速幂求出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;
}
忙碌了一个学期的 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代替求和 用加法代替乘法 即可用矩阵乘法求解
②构造的矩阵如下所示
故我们只需计算出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;
}