矩阵快速幂选做题

文章目录

  • 题目一
    • 问题描述
      • 样例输入
      • 样例输出
    • 解题思路
    • 代码
  • 题目二
    • 问题描述
      • 样例输入
      • 样例输出
    • 解题思路
    • 代码

题目一

问题描述

衣食无忧的 Q老师 有一天突发奇想,想要去感受一下劳动人民的艰苦生活。
具体工作是这样的,有 N 块砖排成一排染色,每一块砖需要涂上红、蓝、绿、黄这 4 种颜色中的其中 1 种。且当这 N 块砖中红色和绿色的块数均为偶数时,染色效果最佳。
为了使工作效率更高,Q老师 想要知道一共有多少种方案可以使染色效果最佳,你能帮帮他吗?

input:
第一行为 T,代表数据组数。(1 ≤ T ≤ 100)
接下来 T 行每行包括一个数字 N,代表有 N 块砖。(1 ≤ N ≤ 1e9)
output:
输出满足条件的方案数,答案模 10007。

样例输入

2
1
2

样例输出

2
6

解题思路

首先题目要求我们求解红色和绿色染色均为偶数的方案数,每一个方案都是又一个个的状态拼凑起来的,当前状态和上一个状态有关,明显具有子结构的特点,所以这道题可以用动态规划来解,但是N的范围很大,所以要使用矩阵快速幂来进行优化,将算法降到log级别。

首先对于状态定义,我们使用一个数组a[i]来表示i个格子,红绿均为偶数的染色方案,一个数组b[i]表示i个格子,红绿均为奇数的染色方案数,一个数组从c[i]来表示红绿有一个为偶数的染色方案数。转移方程为 a [ i ] = 2 ∗ a [ i − 1 ] + c [ i − 1 ] a[i]=2*a[i-1]+c[i-1] a[i]=2a[i1]+c[i1] b [ i ] = 2 ∗ b [ i − 1 ] + c [ i − 1 ] b[i]=2*b[i-1]+c[i-1] b[i]=2b[i1]+c[i1] c [ i ] = 2 ∗ a [ i − 1 ] + 2 ∗ b [ i − 1 ] + 2 ∗ c [ i − 1 ] c[i]=2*a[i-1]+2*b[i-1]+2*c[i-1] c[i]=2a[i1]+2b[i1]+2c[i1]
其中第一条表示,当前第i块我们可以染蓝黄两种颜色,所以红绿两种颜色还是偶数,或者从红绿有一个为偶数在添加其中为奇数的那个颜色。对于第二条,当前第i块我们可以染蓝黄两种颜色,所以红绿两种颜色还是奇数,或者是从红绿有一个为偶数在添加其中为偶数的那个颜色,第三条,第i块可以染红绿两种颜色,这样之前两种颜色都为偶数或都为奇数都变成了一个偶数的状态,或者染蓝黄,这样原来的一个偶数状态保持不变。所以矩阵为
[ 2 0 1 0 2 1 2 2 2 ] \left[ \begin{matrix} 2 & 0 & 1 \\ 0 & 2 & 1 \\ 2 & 2 & 2 \end{matrix} \right] 202022112然后根据这个矩阵,进行矩阵快速幂

代码

#include
#include
using namespace std;
struct ma{
	int x[3][3];
	ma operator * (const ma &mm)const{
		ma res;//重载*
		for(int i=0;i<3;i++)
		{
			for(int j=0;j<3;j++)
			{
				res.x[i][j]=0;
				for(int k=0;k<3;k++)
				{
					res.x[i][j]+=x[i][k]*mm.x[k][j]%10007;//矩阵带模乘
					res.x[i][j]%=10007;	
				}	
			}
		}
		return res;
	} 
	ma(){//构造函数
		memset(x,0,sizeof(x));
	}
	ma(const ma &mm)
	{//复制构造
		for(int i=0;i<3;i++)
		{
			for(int j=0;j<3;j++)
				x[i][j]=mm.x[i][j];
		}
	}
};
ma quick(ma a,int n)
{//快速幂
	ma res;
	for(int i=0;i<3;i++)
	{
		for(int j=0;j<3;j++)
		{//初始化单位矩阵
			if(i==j)
				res.x[i][j]=1;
			else 
				res.x[i][j]=0;
		}	
	} 
	while(n)
	{//快速幂
		if(n&1)
			res=res*a;
		a=a*a;
		n>>=1;
	}
	return res;
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		cin>>n;
		ma temp;
		temp.x[0][0]=2;temp.x[0][1]=0;temp.x[0][2]=1;
		temp.x[1][0]=0;temp.x[1][1]=2;temp.x[1][2]=1;
		temp.x[2][0]=temp.x[2][1]=temp.x[2][2]=2;//初始化矩阵
		ma res=quick(temp,n-1);
		cout<<(2*res.x[0][0]+2*res.x[0][2])%10007<<endl;
	}	
} 

题目二

问题描述

忙碌了一个学期的 Q老师 决定奖励自己 N 天假期。
假期中不同的穿衣方式会有不同的快乐值。
已知 Q老师 一共有 M 件衬衫,且如果昨天穿的是衬衫 A,今天穿的是衬衫 B,则 Q老师 今天可以获得 f[A][B] 快乐值。
在 N 天假期结束后,Q老师 最多可以获得多少快乐值?

input:
忙碌了一个学期的 Q老师 决定奖励自己 N 天假期。
假期中不同的穿衣方式会有不同的快乐值。
已知 Q老师 一共有 M 件衬衫,且如果昨天穿的是衬衫 A,今天穿的是衬衫 B,则 Q老师 今天可以获得 f[A][B] 快乐值。
在 N 天假期结束后,Q老师 最多可以获得多少快乐值?
output:
每组测试样例输出一行,表示Q老师可以获得的最大快乐值。

样例输入

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

样例输出

2
9

解题思路

首先这样也是一道动态规划题,但是由于数据范围过大,也需要矩阵快速幂优化。首先我们令数组g[i][j]表示第i天,穿的衣服为j所获得的快乐值总和,转移方程为g[i][j]=max(g[i-1][k]+f[k][j]),1≤k≤M,我们发现这个转移方程是一个取最大值,并且中间两项是加不是乘,则我们在矩阵快速幂优化的时候要将矩阵乘的定义稍微修改一下即可。

代码

#include
#include
#include
using namespace std;
long long int n,m;
struct ma{
	long long int x[110][110];
	ma operator * (const ma &mm)const{
		ma res;//乘法
		for(int i=0;i<m;i++)
		{
			for(int j=0;j<m;j++)
			{
				res.x[i][j]=0;
				for(int k=0;k<m;k++)
				{
					res.x[i][j]=max(res.x[i][j],x[i][k]+mm.x[k][j]);
					//取max,并且亮相是求和
				}	
			}
		}
		return res;
	} 
	ma(){
		memset(x,0,sizeof(x));
	}
	ma(const ma &mm)
	{
		for(int i=0;i<m;i++)
		{
			for(int j=0;j<m;j++)
				x[i][j]=mm.x[i][j];
		}
	}
};
ma quick(ma a,int nn)
{
	ma res;//矩阵快速幂
	for(int i=0;i<m;i++)
	{
		for(int j=0;j<m;j++)
		{
			if(i==j)
				res.x[i][j]=0;//初始化矩阵,由于不是乘,相应的初始化应改变
			else 
				res.x[i][j]=-10000;
		}	
	} 
	while(nn)
	{
		if(nn&1)
			res=res*a;
		a=a*a;
		nn>>=1;
	}
	return res;
}
int main()
{
	while(cin>>n>>m)
	{
		ma temp;
		for(int i=0;i<m;i++)
		{
			for(int j=0;j<m;j++)
			{
				cin>>temp.x[i][j];
			}
		}
		ma result=quick(temp,n-1);
		long long int ans=0;
		for(int i=0;i<m;i++)
		{
			for(int j=0;j<m;j++)
				ans=max(ans,result.x[i][j]);
		}
		cout<<ans<<endl;
	} 
} 

你可能感兴趣的:(矩阵快速幂选做题)