算法拾遗二十九单调栈结构续及快速矩阵幂

算法拾遗二十九单调栈结构续及快速矩阵幂

      • 题目一
      • 求斐波那契数列矩阵乘法的方法
      • 题目二-返回N年后牛的数量
        • 小总结
      • 题目三
      • 题目四

题目一

算法拾遗二十九单调栈结构续及快速矩阵幂_第1张图片
算法拾遗二十九单调栈结构续及快速矩阵幂_第2张图片
假设有如上图数组,找到10位置的7与他相邻的且比他小的两个数为5位置的5以及15位置的4。
我们现在需要知道6到14位置的子数组中有多少是以7作为最小值的。
6-10,6-11,6-12,6-13,6-14位置都是要跨过10位置的7,才是以7作为最小值。
算法拾遗二十九单调栈结构续及快速矩阵幂_第3张图片
如上图以10作为子数组最小值的时候,一共有25种组合,得出来的累加和为25乘以7。
推广:
假设来到i位置对应的值是x,假设左边离我最近的比我小的位置是k位置的y,右边离我最近的比我小的位置是j位置的z。产生多少累加和?
算法拾遗二十九单调栈结构续及快速矩阵幂_第4张图片
k+1位置到i位置的数字的个数为i-(k+1)+1=i-k去乘以(j到i有多少个结束位置)(j-i【j到不了】)最后再乘以x就是这段区域的累加和。

如上方式是对应于无重复值的情况,那么如果有重复值呢?

算法拾遗二十九单调栈结构续及快速矩阵幂_第5张图片
以3位置的3做最小值的子数组,推导范围为1-6范围,以7位置的3做最小值的子数组推导范围为1-10位置。以11位置的3做最小值的子数组推导范围为1-12,这样是为了去重,防止算重。前面位置是全量的,结尾位置是去重算了的

// 测试链接:https://leetcode.cn/problems/sum-of-subarray-minimums/
// subArrayMinSum1是暴力解
// subArrayMinSum2是最优解的思路
// sumSubarrayMins是最优解思路下的单调栈优化
// Leetcode上不要提交subArrayMinSum1、subArrayMinSum2方法,因为没有考虑取摸
// Leetcode上只提交sumSubarrayMins方法,时间复杂度O(N),可以直接通过
public class SumOfSubarrayMinimums {

	public static int subArrayMinSum1(int[] arr) {
		int ans = 0;
		for (int i = 0; i < arr.length; i++) {
			for (int j = i; j < arr.length; j++) {
				int min = arr[i];
				for (int k = i + 1; k <= j; k++) {
					min = Math.min(min, arr[k]);
				}
				ans += min;
			}
		}
		return ans;
	}

	// 没有用单调栈
	public static int subArrayMinSum2(int[] arr) {
		// left[i] = x : arr[i]左边,离arr[i]最近,<=arr[i],位置在x
		int[] left = leftNearLessEqual2(arr);
		// right[i] = y : arr[i]右边,离arr[i]最近,< arr[i],的数,位置在y
		int[] right = rightNearLess2(arr);
		int ans = 0;
		for (int i = 0; i < arr.length; i++) {
			//左边到不了的算一个开头数量
			int start = i - left[i];
			//右边到不了的位置算一个总的结尾数量
			int end = right[i] - i;
			ans += start * end * arr[i];
		}
		return ans;
	}

	public static int[] leftNearLessEqual2(int[] arr) {
		int N = arr.length;
		int[] left = new int[N];
		for (int i = 0; i < N; i++) {
			int ans = -1;
			for (int j = i - 1; j >= 0; j--) {
				if (arr[j] <= arr[i]) {
					ans = j;
					break;
				}
			}
			left[i] = ans;
		}
		return left;
	}

	public static int[] rightNearLess2(int[] arr) {
		int N = arr.length;
		int[] right = new int[N];
		for (int i = 0; i < N; i++) {
			int ans = N;
			for (int j = i + 1; j < N; j++) {
				if (arr[i] > arr[j]) {
					ans = j;
					break;
				}
			}
			right[i] = ans;
		}
		return right;
	}

	public static int sumSubarrayMins(int[] arr) {
		int[] stack = new int[arr.length];
		int[] left = nearLessEqualLeft(arr, stack);
		int[] right = nearLessRight(arr, stack);
		long ans = 0;
		for (int i = 0; i < arr.length; i++) {
			long start = i - left[i];
			long end = right[i] - i;
			ans += start * end * (long) arr[i];
			ans %= 1000000007;
		}
		return (int) ans;
	}

	public static int[] nearLessEqualLeft(int[] arr, int[] stack) {
		int N = arr.length;
		int[] left = new int[N];
		int size = 0;
		for (int i = N - 1; i >= 0; i--) {
			while (size != 0 && arr[i] <= arr[stack[size - 1]]) {
				left[stack[--size]] = i;
			}
			stack[size++] = i;
		}
		while (size != 0) {
			left[stack[--size]] = -1;
		}
		return left;
	}

	public static int[] nearLessRight(int[] arr, int[] stack) {
		int N = arr.length;
		int[] right = new int[N];
		int size = 0;
		for (int i = 0; i < N; i++) {
			while (size != 0 && arr[stack[size - 1]] > arr[i]) {
				right[stack[--size]] = i;
			}
			stack[size++] = i;
		}
		while (size != 0) {
			right[stack[--size]] = N;
		}
		return right;
	}

	public static int[] randomArray(int len, int maxValue) {
		int[] ans = new int[len];
		for (int i = 0; i < len; i++) {
			ans[i] = (int) (Math.random() * maxValue) + 1;
		}
		return ans;
	}

	public static void printArray(int[] arr) {
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
	}

	public static void main(String[] args) {
		int maxLen = 100;
		int maxValue = 50;
		int testTime = 100000;
		System.out.println("测试开始");
		for (int i = 0; i < testTime; i++) {
			int len = (int) (Math.random() * maxLen);
			int[] arr = randomArray(len, maxValue);
			int ans1 = subArrayMinSum1(arr);
			int ans2 = subArrayMinSum2(arr);
			int ans3 = sumSubarrayMins(arr);
			if (ans1 != ans2 || ans1 != ans3) {
				printArray(arr);
				System.out.println(ans1);
				System.out.println(ans2);
				System.out.println(ans3);
				System.out.println("出错了!");
				break;
			}
		}
		System.out.println("测试结束");
	}

}

求斐波那契数列矩阵乘法的方法

如果一个式子有严格的递推过程,都有logn复杂度的递推方法
算法拾遗二十九单调栈结构续及快速矩阵幂_第6张图片
算法拾遗二十九单调栈结构续及快速矩阵幂_第7张图片
算法拾遗二十九单调栈结构续及快速矩阵幂_第8张图片
1a+1c = 2
1b+1d = 1
算法拾遗二十九单调栈结构续及快速矩阵幂_第9张图片
2a +1c = 3
2b+d=2
得到:
a=1
c=1
b=1
d=0

算法拾遗二十九单调栈结构续及快速矩阵幂_第10张图片
如何让矩阵的次方算的足够快:
以10的七十五次方为例:
75的二进制:1001011
定义一个t = 10
每次t和自己乘:10的二次方,10的四次方 10的八次方。。。。
ans = 1
10的一次放 * 10的二次方 乘以10的八次方再乘以10的六十四次方,按位去转换。
算法拾遗二十九单调栈结构续及快速矩阵幂_第11张图片
只需要logN的复杂度就可以求出最后的次方值。

如果是矩阵的75次方:
算法拾遗二十九单调栈结构续及快速矩阵幂_第12张图片
得到单位矩阵为对角线互为0,1
假设F(n):
算法拾遗二十九单调栈结构续及快速矩阵幂_第13张图片
减到最到底的是3,所以这是一个3阶的递推
算法拾遗二十九单调栈结构续及快速矩阵幂_第14张图片
乘以一个3乘3的矩阵,推广:
算法拾遗二十九单调栈结构续及快速矩阵幂_第15张图片
算法拾遗二十九单调栈结构续及快速矩阵幂_第16张图片
如果是一个i的矩阵,则乘以一个i乘i矩阵的n-i次方,递推式不变
算法拾遗二十九单调栈结构续及快速矩阵幂_第17张图片

	public static int f1(int n) {
		if (n < 1) {
			return 0;
		}
		if (n == 1 || n == 2) {
			return 1;
		}
		return f1(n - 1) + f1(n - 2);
	}

	public static int f2(int n) {
		if (n < 1) {
			return 0;
		}
		if (n == 1 || n == 2) {
			return 1;
		}
		int res = 1;
		int pre = 1;
		int tmp = 0;
		for (int i = 3; i <= n; i++) {
			tmp = res;
			res = res + pre;
			pre = tmp;
		}
		return res;
	}

	// O(logN)
	public static int f3(int n) {
		if (n < 1) {
			return 0;
		}
		if (n == 1 || n == 2) {
			return 1;
		}
		// [ 1 ,1 ]
		// [ 1, 0 ]
		//单位矩阵
		int[][] base = { 
				{ 1, 1 }, 
				{ 1, 0 } 
				};
		int[][] res = matrixPower(base, n - 2);
		//(0,1)(1,0)加上得到第n项 根据递推式
		return res[0][0] + res[1][0];
	}

	public static int[][] matrixPower(int[][] m, int p) {
		int[][] res = new int[m.length][m[0].length];
		//对角线上全填入1
		for (int i = 0; i < res.length; i++) {
			res[i][i] = 1;
		}
		// res = 矩阵中的1
		int[][] t = m;// 矩阵1次方
		for (; p != 0; p >>= 1) {
			if ((p & 1) != 0) {
				res = product(res, t);
			}
			t = product(t, t);
		}
		return res;
	}

	// 两个矩阵乘完之后的结果返回
	public static int[][] product(int[][] a, int[][] b) {
		int n = a.length;
		int m = b[0].length;
		int k = a[0].length; // a的列数同时也是b的行数
		int[][] ans = new int[n][m];
		for(int i = 0 ; i < n; i++) {
			for(int j = 0 ; j < m;j++) {
				for(int c = 0; c < k; c++) {
					ans[i][j] += a[i][c] * b[c][j];
				}
			}
		}
		return ans;
	}

题目二-返回N年后牛的数量

算法拾遗二十九单调栈结构续及快速矩阵幂_第18张图片
算法拾遗二十九单调栈结构续及快速矩阵幂_第19张图片
第五年6头牛,第六年9头牛。
得出F(n) = F(n-1)+F(n-3) :
今年的牛由去年的牛活下来留到今年和新出生的牛构成(3年前有多少小牛都能帮着生小牛,而三年中的牛都生不了)
行列式推导:
算法拾遗二十九单调栈结构续及快速矩阵幂_第20张图片
算法拾遗二十九单调栈结构续及快速矩阵幂_第21张图片
算法拾遗二十九单调栈结构续及快速矩阵幂_第22张图片
算法拾遗二十九单调栈结构续及快速矩阵幂_第23张图片
最后得到F(n)=3乘以x+2乘以y+1乘以z

	//递归方法
	public static int c1(int n) {
		if (n < 1) {
			return 0;
		}
		if (n == 1 || n == 2 || n == 3) {
			return n;
		}
		return c1(n - 1) + c1(n - 3);
	}

	//递推方法
	public static int c2(int n) {
		if (n < 1) {
			return 0;
		}
		if (n == 1 || n == 2 || n == 3) {
			return n;
		}
		int res = 3;
		int pre = 2;
		int prepre = 1;
		int tmp1 = 0;
		int tmp2 = 0;
		for (int i = 4; i <= n; i++) {
			tmp1 = res;
			tmp2 = pre;
			res = res + prepre;
			pre = tmp1;
			prepre = tmp2;
		}
		return res;
	}
  //矩阵快速幂
	public static int c3(int n) {
		if (n < 1) {
			return 0;
		}
		if (n == 1 || n == 2 || n == 3) {
			return n;
		}
		int[][] base = { 
				{ 1, 1, 0 }, 
				{ 0, 0, 1 }, 
				{ 1, 0, 0 } };
		int[][] res = matrixPower(base, n - 3);
		return 3 * res[0][0] + 2 * res[1][0] + res[2][0];
	}

	public static int[][] matrixPower(int[][] m, int p) {
		int[][] res = new int[m.length][m[0].length];
		//对角线上全填入1
		for (int i = 0; i < res.length; i++) {
			res[i][i] = 1;
		}
		// res = 矩阵中的1
		int[][] t = m;// 矩阵1次方
		for (; p != 0; p >>= 1) {
			if ((p & 1) != 0) {
				res = product(res, t);
			}
			t = product(t, t);
		}
		return res;
	}

	// 两个矩阵乘完之后的结果返回
	public static int[][] product(int[][] a, int[][] b) {
		int n = a.length;
		int m = b[0].length;
		int k = a[0].length; // a的列数同时也是b的行数
		int[][] ans = new int[n][m];
		for(int i = 0 ; i < n; i++) {
			for(int j = 0 ; j < m;j++) {
				for(int c = 0; c < k; c++) {
					ans[i][j] += a[i][c] * b[c][j];
				}
			}
		}
		return ans;
	}

小总结

由前到后已学模型:
1、二叉树递归套路
2、从左往右尝试模型
3、范围尝试模型
4、样本对应模型
5、业务限制模型
6、斐波那契数列的矩阵乘法模型

题目三

算法拾遗二十九单调栈结构续及快速矩阵幂_第24张图片
算法拾遗二十九单调栈结构续及快速矩阵幂_第25张图片
在这里插入图片描述
只有101,110,111达标

public class ZeroLeftOneStringNumber {

	public static int getNum1(int n) {
		if (n < 1) {
			return 0;
		}
		return process(1, n);
	}

	public static int process(int i, int n) {
		if (i == n - 1) {
			return 2;
		}
		if (i == n) {
			return 1;
		}
		return process(i + 1, n) + process(i + 2, n);
	}

	public static int getNum2(int n) {
		if (n < 1) {
			return 0;
		}
		if (n == 1) {
			return 1;
		}
		int pre = 1;
		int cur = 1;
		int tmp = 0;
		for (int i = 2; i < n + 1; i++) {
			tmp = cur;
			cur += pre;
			pre = tmp;
		}
		return cur;
	}

	public static int getNum3(int n) {
		if (n < 1) {
			return 0;
		}
		if (n == 1 || n == 2) {
			return n;
		}
		int[][] base = { { 1, 1 }, { 1, 0 } };
		int[][] res = matrixPower(base, n - 2);
		return 2 * res[0][0] + res[1][0];
	}
	
	
	
	
	
	
	public static int fi(int n) {
		if (n < 1) {
			return 0;
		}
		if (n == 1 || n == 2) {
			return 1;
		}
		int[][] base = { { 1, 1 }, 
				         { 1, 0 } };
		int[][] res = matrixPower(base, n - 2);
		return res[0][0] + res[1][0];
	}

	
	
	
	public static int[][] matrixPower(int[][] m, int p) {
		int[][] res = new int[m.length][m[0].length];
		for (int i = 0; i < res.length; i++) {
			res[i][i] = 1;
		}
		int[][] tmp = m;
		for (; p != 0; p >>= 1) {
			if ((p & 1) != 0) {
				res = product(res, tmp);
			}
			tmp = product(tmp, tmp);
		}
		return res;
	}

	// 两个矩阵乘完之后的结果返回
	public static int[][] product(int[][] a, int[][] b) {
		int n = a.length;
		int m = b[0].length;
		int k = a[0].length; // a的列数同时也是b的行数
		int[][] ans = new int[n][m];
		for(int i = 0 ; i < n; i++) {
			for(int j = 0 ; j < m;j++) {
				for(int c = 0; c < k; c++) {
					ans[i][j] += a[i][c] * b[c][j];
				}
			}
		}
		return ans;
	}

	public static void main(String[] args) {
		for (int i = 0; i != 20; i++) {
			System.out.println(getNum1(i));
			System.out.println(getNum2(i));
			System.out.println(getNum3(i));
			System.out.println("===================");
		}

	}
}

题目四

算法拾遗二十九单调栈结构续及快速矩阵幂_第26张图片
算法拾遗二十九单调栈结构续及快速矩阵幂_第27张图片
F(n)
算法拾遗二十九单调栈结构续及快速矩阵幂_第28张图片
如果按照上述方式摆放,则是一个F(n-1)
算法拾遗二十九单调栈结构续及快速矩阵幂_第29张图片
如果按照上述方式摆放,则是F(n-2)
F(n) = F(n-1)+F(n-2)

你可能感兴趣的:(算法,矩阵,数据结构)