第196场周赛
##1.判断能否形成等差数列
先将数组排序,再利用等差数列的性质遍历一遍。
1 | class Solution { |
2 | public boolean canMakeArithmeticProgression(int[] arr) { |
3 | if(arr==null || arr.length==0){ |
4 | return false; |
5 | } |
6 | if(arr.length==1){ |
7 | return true; |
8 | } |
9 | Arrays.sort(arr); |
10 | int n = arr.length; |
11 | int cha = arr[1]-arr[0]; |
12 | for(int i=2; i<n; i++){ |
13 | if(arr[i]-arr[i-1]!=cha){ |
14 | return false; |
15 | } |
16 | } |
17 | return true; |
18 | } |
19 | } |
复杂度分析:
- 时间复杂度:$O(nlogn+n)$。
- 空间复杂度:$O(1)$。
2.所有蚂蚁掉下来前的最后一刻
两个蚂蚁相撞之后会互相调头,其实只要想成如果每只蚂蚁都长得一模一样,那么蚂蚁碰撞的调头就等于穿透了。知道了这一点,那么就可以直接让蚂蚁直接穿透爬行就好了,那么题目就变成了求单只最晚落地的蚂蚁,与碰撞无关。left
数组中的蚂蚁求出到左边最晚时间,right
数组中的蚂蚁求出到右边最晚时间,之后两者取较大值即可。
1 | class Solution { |
2 | public int getLastMoment(int n, int[] left, int[] right) { |
3 | int ans = -1; |
4 | for(int i=0; i<left.length; i++){ |
5 | ans = Math.max(ans, left[i]); |
6 | } |
7 | for(int i=0; i<right.length; i++){ |
8 | ans = Math.max(ans, n-right[i]); |
9 | } |
10 | return ans; |
11 | } |
12 | } |
时间复杂度:
- 时间复杂度:$O(n)$,$n$为数组$left$和$right$的长度。
- 空间复杂度:$O(1)$。
3.统计全1子矩形
首先动态规划,计算每个点前面连续的1的个数(行,包括自己),即dp[i][j]=dp[i][j-1]+1
,点为0的点不用计算,直接为0即可。然后开始循环遍历,每次计算以mat[i][j]
为右下角的矩阵个数,并累加到sum
中。
举例子,如下图的矩阵,我们现在计算以mat[2][2]
为右下角所构成的矩阵:
1 | [0,0,1] |
2 | [0,1,1] |
3 | [1,1,1] |
首先是第三行dp[2][2]=3
,所以最小长度为3,sum+=3
。这里计算的矩阵只有第三行元素构成的矩阵:
1 | [1,1,1], [1,1], [1] |
向上遍历,第二行时,dp[1][2]=2
,此时最小长度为2,sum+=2
,这里计算的矩阵是第二、三行元素构成的矩阵:
1 | [1] [1,1] |
2 | [1], [1,1] |
继续向上遍历,第一行时,dp[0][2]=1
,此时最小长度为1,sum+=1
,这里计算的矩阵是第一、二、三行元素构成的矩阵:
1 | [1] |
2 | [1] |
3 | [1] |
该点遍历结束。矩阵循环遍历结束后返回sum
即可。因为这里计数的时候固定了子矩阵的右下角元素,所以不会重复计数。
1 | class Solution { |
2 | public int numSubmat(int[][] mat) { |
3 | int m = mat.length; |
4 | int n = mat[0].length; |
5 | int[][] dp = new int[m][n]; |
6 | int sum = 0; |
7 | for(int i=0; i<m; i++){ |
8 | dp[i][0] = mat[i][0]; |
9 | } |
10 | for(int i=0; i<m; i++){ |
11 | for(int j=0; j<n; j++){ |
12 | if(mat[i][j] == 0){ |
13 | dp[i][j] = 0; |
14 | continue; |
15 | } |
16 | if(j!=0){ |
17 | dp[i][j] = dp[i][j-1] + 1; |
18 | } |
19 | int minlen = n+1; |
20 | for(int k=i; k>=0; k--){ |
21 | minlen = Math.min(minlen, dp[k][j]); |
22 | sum += minlen; |
23 | } |
24 | } |
25 | } |
26 | return sum; |
27 | } |
28 | } |
复杂度分析:
- 时间复杂度:$O(mmn)$。
- 空间复杂度:$O(m*n)$。
最多K次交换相邻数位后得到的最小整数
可以使用贪心策略,即每次交换都先将更小的数如0、1等换到较前面的位置去。这个过程中我们需要做两件事:
- 找到较小数的位置
- 计算交换需要用到的次数
找到较小数的位置我们可以使用哈希表来优化这一搜索的过程,即遍历一遍字符串,记录每个数出现的位置,之后在进行搜索的时候只需要查找哈希表就可得到位置。计算交换需要用到的次数不可以直接两个索引相减得到,因为我们实际上并没有交换原字符串中两个数的位置。需要使用树状数组来计算,首先初始化树状数组,在单点更新的时候都加1,即初始化交换次数,在每次交换的时候,就可以利用树状数组查询交换次数,并进行单点更新,此时单点更新需要减1,因为在$i$和$j(i<j)$进行交换的时候,$[i,j-1]$之间的数都往右移了一位,那么这些数之后需要交换的次数就可以因为这次交换而减1,例如2,3,4,1
,再进行过一次交换后变为1,2,3,4
,此时就不需要再次交换了,因为2,3,4
在之前的一次交换中已经向右移动了一位,也相当于进行了交换。
1 | class Solution { |
2 | public String minInteger(String num, int k) { |
3 | char[] s = num.toCharArray(); |
4 | int n = s.length; |
5 | |
6 | IntergerBIT bit = new IntergerBIT(s.length); |
7 | for(int i=1; i<=n; i++){ |
8 | bit.update(i, 1); |
9 | } |
10 | Deque<Integer>[] dq = new Deque[10]; |
11 | for(int i=0; i<10; i++){ |
12 | dq[i] = new ArrayDeque(n); |
13 | } |
14 | for(int i=0; i<n; i++){ |
15 | dq[s[i]-'0'].add(i); |
16 | } |
17 | StringBuilder ans = new StringBuilder(n); |
18 | for(int i=0; i<n; i++){ |
19 | for(int j=0; j<10; j++){ |
20 | if(!dq[j].isEmpty() && bit.query(dq[j].getFirst())<=k){ |
21 | k -= bit.query(dq[j].getFirst()); |
22 | bit.update(dq[j].getFirst()+1, -1); |
23 | dq[j].removeFirst(); |
24 | ans.append((char)(j+'0')); |
25 | break; |
26 | } |
27 | } |
28 | } |
29 | return ans.toString(); |
30 | } |
31 | } |
32 | |
33 | public class IntergerBIT { |
34 | int[] data; |
35 | int n; |
36 | |
37 | /** |
38 | * 创建大小A[1...n] |
39 | */ |
40 | public IntergerBIT(int n){ |
41 | this.n = n; |
42 | this.data = new int[n+1]; |
43 | } |
44 | /** |
45 | * 查询A[1]+A[2]+...+A[i] |
46 | */ |
47 | public int query(int i) { |
48 | int sum = 0; |
49 | while(i > 0){ |
50 | sum += data[i]; |
51 | i -= lowbit(i); |
52 | } |
53 | return sum; |
54 | } |
55 | |
56 | public int query(int l, int r) { |
57 | return query(r) - query(l - 1); |
58 | } |
59 | |
60 | /** |
61 | * 将A[i]更新为A[i]+mod |
62 | */ |
63 | public void update(int i, int mod) { |
64 | if(i <= 0){ |
65 | return; |
66 | } |
67 | while(i<=n){ |
68 | data[i] += mod; |
69 | i += lowbit(i); |
70 | } |
71 | } |
72 | |
73 | /** |
74 | * 将A全部清0 |
75 | */ |
76 | public void clear() { |
77 | Arrays.fill(data, 0); |
78 | } |
79 | |
80 | |
81 | public String toString() { |
82 | StringBuilder builder = new StringBuilder(); |
83 | for (int i = 1; i <= n; i++) { |
84 | builder.append(query(i) - query(i - 1)).append(' '); |
85 | } |
86 | return builder.toString(); |
87 | } |
88 | |
89 | public static int lowbit(int x) { |
90 | return x & (-x); |
91 | } |
92 | } |
复杂度分析:
- 时间复杂度:$O(nlogn)$。
- 空间复杂度:$O(n)$。