快速幂-矩阵快速幂-线性dp优化

快速幂

当求某一正整数 a a a n n n 次幂 a n a^n an 时,常用的做法是将 a a a 连续乘以 n n n 次,其时间复杂度为 O ( n ) O(n) O(n) 。 事实上,该问题有时间复杂度为 O ( l o g n ) O(logn) O(logn) 的解法。其核心思想是: a n = { a n / 2   ∗   a n / 2 , n  为偶数 a n / 2   ∗   a n / 2   ∗ a , i n  为奇数 a^n= \begin{cases} a^{n/2}\,*\,a^{n/2}, & \text {$n$ 为偶数} \\ a^{n/2}\,*\,a^{n/2}\,*a, & \text{i$n$ 为奇数} \end{cases} an={an/2an/2,an/2an/2a,n 为偶数in 为奇数

代码如下

	/**
	 * 求 a 的 n 次幂
	 * @param a
	 * @param n
	 * @return
	 */
	long pow(int a, int n) {
			long res = 1;
			
			while(n != 0) {
				if(n % 2 == 1) {
					res *= a; 
				}
				n /= 2;
				a *= a;
			}
			
			return res;
		}

矩阵快速幂

将上述的整数乘法改成矩阵乘法,就可快速幂推广至矩阵快速幂。

代码如下

	/**
	 * 矩阵 a 的 n 次幂
	 * @param a 
	 * @param n 
	 * @return
	 */
	int[][] pow(int[][] a, int n) {
		int k = a.length;
		int[][] res = new int[k][k];
		for(int i = 0; i < k; i++) {
			res[i][i] = 1;
		}
		
		while(n != 0) {
			if(n % 2 == 1) {
				res = mul(res, a);
			}
			n /= 2;
			a = mul(a, a);
		}
		
		return res;
	}

	/**
	  * 矩阵乘法
	 * @param a
	 * @param b
	 * @return
	 */
	int[][] mul(int[][] a, int[][] b) {
		int a_row = a.length;
		int b_col = b[0].length;
		int n = a[0].length; //a的列数等于b的行数
		int[][] res = new int[a_row][b_col];
		
		for(int i = 0; i < a_row; i++) {
			for(int j = 0; j < b_col; j++) {
				for(int k = 0; k < n; k++) {
					res[i][j] += a[i][k] * b[k][j];
				}
			}
		}
		
		return res;
	}

线性dp优化

一组 D P DP DP 状态,其实等价于一个向量。而DP状态的转移方程,可以是对一个向量做变形的矩阵。那么本质上从一个向量到另一个状态的向量,是可以通过一个矩阵来做到。矩阵具有结合律,我们可以先对右半部分矩阵用快速幂得到一个终极的变形矩阵,再乘以向量,就可以把 O ( n ) O(n) O(n) 的计算优化到 O ( l o g ( n ) ) O (log (n)) O(log(n))(转自简书——DP的五类优化-快速幂,四边形不等式)

例题:
1、 F i b o n a c c i Fibonacci Fibonacci 数列

递推式: F n = F n − 1 + F n − 2 ,    ( n > 2 ) F_n=F_{n-1}+F_{n-2},\;(n > 2) Fn=Fn1+Fn2,(n>2)
d p dp dp 状态转移方程: d p [ n ] = d p [ n − 1 ] + d p [ n − 2 ] ,    ( n > 2 ) dp[n] = dp[n-1] +dp[n-2],\;(n>2) dp[n]=dp[n1]+dp[n2],(n>2)

F i b o n a c c i Fibonacci Fibonacci 数列的递推式可以得到如下矩阵关系式:
( F n F n − 1 ) = ( 1 1 1 0 ) ( F n − 1 F n − 2 ) = ( 1 1 1 0 ) n − 2 ( F 2 F 1 ) \begin{pmatrix} F_n \\ F_{n-1} \end{pmatrix} = \begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix} \begin{pmatrix} F_{n-1} \\ F_{n-2} \end{pmatrix} = \begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix}^{n-2} \begin{pmatrix} F_2 \\ F_1 \end{pmatrix} (FnFn1)=(1110)(Fn1Fn2)=(1110)n2(F2F1)
故该问题转换成矩阵的 n n n 次幂,实现代码如下:

	/**
	 * 求f(n)
	 * @param n
	 * @return
	 */
	int fibonacci(int n) {
		if(n == 0) {
			return 0;
		}
		if(n <= 2) {
			return 1;
		}
		
		int[][] initial = {{1}, {1}}; //f(2), f(1)
		int[][] transition = {{1, 1}, {1, 0}}; //转换矩阵
		int[][] result = new int[2][2]; //结果矩阵 f(n), f(n-1)
		result = mul(pow(transition, n - 2), initial);
		
		return result[0][0];
	}

注: m u l mul mul 函数和 p o w pow pow 函数均为矩阵快速幂部分中的对应函数

现在有一个问题——如何得到转换矩阵?

未想到通解。。。

2、CCF202006第四题 1246 ( d i g i t s ) 1246(digits) 1246(digits)

后续更新。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值