裴蜀定理
对于任何整数a, b
和他们的最大公约数d
,关于未知数x, y
的线性不定方程(裴蜀等式 )。若a, b
是整数,且gcd(a, b) = d
,那么对于任意的整数x,y
,ax+by
都一定是d
的倍数,特别的,一定存在整数x,y
,使ax+by=d
成立
水壶问题
372 超级次方
你的任务是计算 ab 对 1337 取模,a 是一个正整数,b 是一个非常大的正整数且会以数组形式给出。
示例 :
1 2 3 4 5 输入: a = 2, b = [3] 输出: 8 输入: a = 2, b = [1,0] 输出: 1024
问题本身并不难,一下就能想到快速幂。但是就是那个位上的权值一下子没想清楚…
r e s = a 1 0 k b 0 + 1 0 k − 1 b 1 + 1 0 k − 2 b 2 + . . . + 1 0 0 b k res = a^{10^kb_0+10^{k-1}b_1+10^{k-2}b_2+...+10^0b_k} r e s = a 1 0 k b 0 + 1 0 k − 1 b 1 + 1 0 k − 2 b 2 + . . . + 1 0 0 b k
r e s = a b 0 res = a^{b_0} r e s = a b 0
r e s = r e s 10 ∗ a b 1 = a 10 b 0 + b 1 res = res^{10} * a^{b_1} = a^{10b_0 + b_1} r e s = r e s 1 0 ∗ a b 1 = a 1 0 b 0 + b 1
r e s = r e s 10 ∗ a b 2 = ( a 10 b 0 + b 1 ) 10 ∗ a b 2 = a 1 0 2 b 0 + 10 b 1 + b 2 res = res^{10} * a^{b_2} = (a^{10b_0 + b_1})^{10} * a^{b_2} = a^{10^{2}b_0 + 10b_1+b_2} r e s = r e s 1 0 ∗ a b 2 = ( a 1 0 b 0 + b 1 ) 1 0 ∗ a b 2 = a 1 0 2 b 0 + 1 0 b 1 + b 2
…
r e s = a 1 0 k b 0 + 1 0 k − 1 b 1 + 1 0 k − 2 b 2 + . . . + 1 0 0 b k res = a^{10^kb_0+10^{k-1}b_1+10^{k-2}b_2+...+10^0b_k} r e s = a 1 0 k b 0 + 1 0 k − 1 b 1 + 1 0 k − 2 b 2 + . . . + 1 0 0 b k
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ll binaryPow (ll a, ll b, ll m) { ll ans = 1 ; while (b > 0 ) { if (b & 1 ) { ans = ans * a % m; } a = a * a % m; b >>= 1 ; } return ans; } int superPow (int a, vector <int >& b) { int n = b.size(); ll check[10 ] = {0 }; ll res = 1 ; a = a % N; for (int i = 0 ; i < 10 ; i++) { check[i] = binaryPow(a, i, N); } for (int i = 0 ; i < n; i++) { res = (binaryPow(res, 10 , N) * check[b[i]]) % N; } return int (res); }
PS: 划重点,上面的快速幂!
373 查找和最小的K对数字
给定两个以升序排列的整形数组 nums1 和 nums2, 以及一个整数 k。
定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2。
找到和最小的 k 对数字 (u1,v1), (u2,v2) … (uk,vk)。
示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 输入: nums1 = [1,7,11], nums2 = [2,4,6], k = 3 输出: [1,2],[1,4],[1,6] 解释: 返回序列中的前 3 对数: [1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6] 输入: nums1 = [1,1,2], nums2 = [1,2,3], k = 2 输出: [1,1],[1,1] 解释: 返回序列中的前 2 对数: [1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3] 输入: nums1 = [1,2], nums2 = [3], k = 3 输出: [1,3],[2,3] 解释: 也可能序列中所有的数对都被返回:[1,3],[2,3]
思路:使用优先队列 和位置指针 快速求解
首先,在遇到这种求前k个最大(最小)的问题时候,很自然的想到使用堆来减少时间复杂度。这里,使用优先级队列priority_queue
来达到堆的效果。
第二,由于 nums1
和 nums2
都是有序的,求前k
个最小的则和类似一个指针从左到右不断滑动的过程,我们可以让数组1作为base
,用另一个数组(长度和num1
相等,,即代码中的check[]
)来记录和从小到大过程中num1
中每个元素 的另一半现在在num2
数组中的位置。 每次循环找出是和最小的那一对,然后让那一对num2
数组中的位置往前走一步,然后再次比较选出最小(使用优先级队列)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 typedef pair<int , int > pp;vector <vector <int > > kSmallestPairs(vector <int >& nums1, vector <int >& nums2, int k) { vector <vector <int > > res; priority_queue<pp, vector <pp>, greater<pp> > q; int i = 0 , j = 0 , n1 = nums1.size(), n2 = nums2.size(); if (!n1 || !n2) { return res; } int check[n1] = {0 }; for (int m = 0 ; m < n1; m++) { q.push(make_pair(nums1[m] + nums2[0 ], m)); } k = min(k, n1 * n2); while (k--) { pair<int , int > p = q.top(); int index = p.second; q.pop(); vector <int > temp; temp.push_back(nums1[index]); temp.push_back(nums2[check[index]++]); if (index < n1 && check[index] < n2) { q.push(make_pair(nums1[index] + nums2[check[index]], index)); } res.push_back(temp); } return res; }
378 有序矩阵中第k小的元素
给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第k小的元素。
请注意,它是排序后的第k小元素,而不是第k个元素。
示例:
1 2 3 4 5 6 7 8 matrix = [ [ 1, 5, 9], [10, 11, 13], [12, 13, 15] ], k = 8, 返回 13。
思路:
想明白之后觉得难度还行~~
这边采用二分法。首先左上角的值为最小值(记为left
),右下角的值为最大值(记为right
)。然后取他们的中间值(mid
),然后判断mid
是第几小的元素,如果小于k
,则令right = mid
,如果比k
大,则令left = mid + 1
。不断更新,直到找到第k
小的元素位置。
然后search_less_equal
函数就是用来查找mid
是第几小的元素。
如下图:
首先假设该点位于左下角,然后不断地右移(说明当前元素过大)或者上移(说明当前元素过小),然后在右移之前需要先加上这一列的比当前元素小的个数。
这样直到走到上边界或者右边界的时候就说明已经求出了比当前小的元素的个数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 int search_less_equal (vector <vector <int > >& matrix, int target) { int n = matrix.size(); int i = n - 1 , j = 0 , res = 0 ; while (i >= 0 && j < n) { if (matrix[i][j] <= target) { res += (i + 1 ); ++j; } else { --i; } } return res; } int kthSmallest (vector <vector <int > >& matrix, int k) { int left = matrix[0 ][0 ], right = matrix.back().back(); while (left < right) { int mid = left + (right - left) / 2 ; int cnt = search_less_equal(matrix, mid); if (cnt < k) { left = mid + 1 ; } else { right = mid; } } return left; }
386 字典序排序
给定一个整数 n, 返回从 1 到 n 的字典顺序。
例如,
给定 n =1 3,返回 [1,10,11,12,13,2,3,4,5,6,7,8,9] 。
请尽可能的优化算法的时间复杂度和空间复杂度。 输入的数据 n 小于等于 5,000,000。
思路:
第一种是按照把数字转换成string
类型,然后入优先队列,然后再转换成int
第二种就比较巧妙了!
按照10叉树的前序遍历即可得到结果,如下图,n = 145
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void dfs (vector <int >& res, int num, int n) { for (int i = 0 ; i < 10 && num <= n && res.size() < n; i++, num++) { res.push_back(num); dfs(res, num * 10 , n); } } vector <int > lexicalOrder(int n) { vector <int > res; dfs(res, 1 , n); for (int i = 0 ; i < res.size(); i++) { cout << res[i] << " " ; } return res; }
390 消除游戏
给定一个从1 到 n 排序的整数列表。
首先,从左到右,从第一个数字开始,每隔一个数字进行删除,直到列表的末尾。
第二步,在剩下的数字中,从右到左,从倒数第一个数字开始,每隔一个数字进行删除,直到列表开头。
我们不断重复这两步,从左到右和从右到左交替进行,直到只剩下一个数字。
返回长度为 n 的列表中,最后剩下的数字。
示例:
1 2 3 4 5 6 7 8 9 输入: n = 9, 1 2 3 4 5 6 7 8 9 2 4 6 8 2 6 6 输出: 6
思路:
设n个数时,从左到右开始执行剩下的数为f1(n),将n个数反转后(即:n,n-1,…,1),从左到右执行剩下的数为f2(n)
则,f1(n)和f2(n)剩下的数,在他们在各自原数组中的下标一定一样,即两者剩余的数都是下标i位置处的元素。1,…,n和n,…,1两个相同下标的元素相加为n+1
那么f2(n)的元素怎么得到?对于2n个元素,执行一次从左到右之后,剩余的元素除以2,可以得到1,…,n的元素。
如果继续执行,相当于对1,…,n的元素从右到左开始执行(因为第一次已经从左到右执行了),等价于:对于1,…,n元素从右到左执行的结果*2,即:f 1 ( 2 n ) = 2 ∗ f 2 ( n ) f1(2n) = 2 * f2(n) f 1 ( 2 n ) = 2 ∗ f 2 ( n )
所有得到递推式:$f1(n) + f2(n) = n + 1 又 由 于 又由于 又 由 于 f2(n) * 2 = f1(2n) 所 以 : 所以: 所 以 : f1(n) + f1(2n) / 2 = n + 1$;
用n/2替换n得到:f 1 ( n / 2 ) + f 1 ( n ) / 2 = n / 2 + 1 → f 1 ( n ) = 2 ∗ ( n / 2 + 1 − f 1 ( n / 2 ) ) f1(n/2) + f1(n) / 2 = n / 2 + 1 \rightarrow f1(n) = 2 * (n / 2 + 1 - f1(n / 2)) f 1 ( n / 2 ) + f 1 ( n ) / 2 = n / 2 + 1 → f 1 ( n ) = 2 ∗ ( n / 2 + 1 − f 1 ( n / 2 ) )
1 2 3 4 int lastRemaining (int n) { if (n == 1 ) return 1 ; return 2 * (n / 2 + 1 - lastRemaining(n / 2 )); }