dp加矩阵快速幂,蓝桥杯/2015/初赛(省赛)/C语言A组/9/垒骰子

先关注下斐波那契数列

f(1)=1,f(2)=1,f(n)=f(n-1)+f(n-2),对n,求f(n)

这是一个递推公式,dp的递推公式也是递推公式
O(n)暴力求解
但可以将递推公式转换成矩阵运算再加快速幂来达到O(logn)的加速。

$$ \begin{equation} \begin{bmatrix} f(n) \\ f(n-1) \end{bmatrix} = \begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix} \begin{bmatrix} f(n-1) \\ f(n-2) \end{bmatrix} \end{equation} $$ $$ \begin{equation} \text{因而} \begin{bmatrix} f(n) \\ f(n-1) \end{bmatrix} = \begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix}^{n-2} \begin{bmatrix} 1 \\ 1 \end{bmatrix} \end{equation} $$ $$ \text{其中} \begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix}^{n-2} \text{可以矩阵快速幂来在log(n)时间内计算} $$

可以矩阵快速幂来在log(n)时间内计算 故可以用递推公式转换矩阵,再加矩阵快速幂来计算递推公式,左边一般是列向量,而且甚至可以矩阵(没试过),只要是右边因子是方阵可以乘就可以,这个斐波那契算是强行二维列向量。 这在得到递推公式后,dp,数据大超时的情况下很有效,本题就是这样的一个应用。 *简单源于规整*先转换下条件 i 和 j 不能相邻 等价于 i 和 j 的对面 不能是相邻两个正面朝上的面。例如,1 和 2 不能相邻表示不能存在相邻两个骰子的正面朝上分别为 1 和 5 ,也不能是 2 和 4 。

$$ \begin{equation} g_{i + 1, j} = sum_{all possible k} g_{i, k}\text{表示用 i 个骰子,最后一层正面朝上的为 j 的方案数} \end{equation} $$ 本题,将6维列向量,第i维设为i向上的方案数 f(i)代表那个骰子i向上的方案数 $$ \text{转换} \begin{equation} F{n}= \begin{bmatrix} f(1) \\ f(2) \\ f(3)\\ f(4)\\ f(5)\\ f(6) \end{bmatrix} = A*F{n-1} \end{equation} \text{,A要自己捏,不可能就设零,F(n)代表n个骰子的方案数,第i维设为i向上的方案数,求和就是总方案数。} $$

```cpp #include #include using namespace std; const int MN = 6; typedef long long M[MN][MN]; long long MOD = 1E9+7; void MtM(M a,M b,M res){ M ans={{0}};//{{}}只能用于清零//一层一个括号{0},{{0}} for(int i =0;i>=1; MtM(a,a,a); } } long long qp(long long a,int n){ long long ans=1;//创建单位元 while(n){//1已经是1次方了,没有0次方,零次方直接输出单位元 if(n&1)ans=a*ans; n>>=1; a*=a; a%=MOD; ans%=MOD; } return ans; } int main(){ int n,m; cin>>n>>m; M v; for(int i =0 ;i< MN;i++){ for(int j =0;j>a>>b; a--,b--; v[a][b<3?b+3:b-3]=0; v[b][a<3?a+3:a-3]=0; } M ansM; long long ans=0; qpM(v,n-1,ansM); // pM(ansM); for(int i =0;i付快速幂代码
//计算斐波那契
#include<iostream>
#include<cstring>
using namespace std;
const int MN = 2;
typedef int M[MN][MN];
void MtM(M a,M b,M res){
	M ans={{0}};//{{}}只能用于清零
	for(int i =0;i<MN;i++){
		for(int j =0;j< MN;j++){
			ans[i][j]=0;
			for(int k =0 ;k<MN;k++){
				ans[i][j]+=a[i][k]*b[k][j];
			}
		}
	}
	memcpy(res,ans,sizeof(ans));//重要的小技巧,可以使得MtM(a,a,a)有效
}
void pM(const M a){
	for(int i =0;i< MN;i++){
		for(int j =0;j< MN;j++){
			cout<<a[i][j]<<' ';
		}
		cout<<endl;
	}
}
void qpM(M a, int n,M ans){
	for(int i=0;i<MN;i++){
		for(int j =0;j<MN;j++){ 
			ans[i][j]=0;
			if(i==j)ans[i][j]=1;
		}
	}//创建单位元
	while(n){//1已经是1次方了,没有0次方,零次方直接输出单位元
		if(n&1)MtM(a,ans,ans);
		n>>=1;
		MtM(a,a,a);
	}
}
long long qp(long long a,int n){
	long long ans=1;//创建单位元
	while(n){//1已经是1次方了,没有0次方,零次方直接输出单位元
		if(n&1)ans=a*ans;
		n>>=1;
		a*=a;
	}
	return ans;
}
int main(){
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	int n;
	cin>>n;
	if(n<=2){cout<<1<<endl;
		return 0;
	}
	n-=2;
	M ans={{0}};
	M v{ 1,1,
		1,0 };
	qpM(v,n,ans);
	pM(ans);
	cout<<ans[0][1]+ans[0][0]<<endl;
	return 0;
}
posted @ 2017-04-07 14:51  xsthunder  阅读(437)  评论(0编辑  收藏  举报