由于今天晚了20min,结果本来能ak的题目,没法ak了。伤心…排名不是特别好看,实在因为手速太慢。
题目四: 使数组相似的最少操作次数
能看到是第一个做的这个题,也算是一点小心机,如果不会做,就直接放弃提交了,保一下名次嘛。
首先分析一下题目的一个操作
nums[i] += 2, nums[j] -= 2, 要求i != j。那么这就可以看成对两个数组分别操作,也就是:
- nums[i] += 2
- target[j] += 2
- i != j
这样每一次操作,就是对两个数组中的不同下标数字分别加上2。题目说一定满足可以把nums变成target,就会满足下面的性质
- nums中奇数个数和target中的奇数个数一样多,偶数也一样多。因为奇数 + 2不可能变成偶数,偶数 + 2也不可能变成奇数。
- target中比nums中大的数字差值的和等于target中比nums中小的数字差值的和。
有了上面的性质,可以考虑分成下面的步骤:
- 分成奇偶进行计算。也就是对应分成两组,numsj和targetj,numso和targeto。
- 只考虑大于的差值,或者只考虑小于的差值就行了,因为是相等的。这里只考虑target大于nums的部分。
如果要求数组操作次数最少。那么第一个直观的想法,就是nums[i]变成距离target中最近的,也就是排序后坐标对应的,也就是nums[i] 变成target[i]就行了。
- 屎山代码
public long makeSimilar(int[] nums, int[] target) {
// 1. 排序
Arrays.sort(nums);
Arrays.sort(target);
// 2. 分成奇和偶
List<Integer>numsj = new ArrayList<>();
List<Integer>numso = new ArrayList<>();
List<Integer>to = new ArrayList<>();
List<Integer>tj = new ArrayList<>();
for (int x : nums) {
if (x % 2 == 0) numso.add(x);
else numsj.add(x);
}
for (int x : target) {
if (x % 2 == 0) to.add(x);
else tj.add(x);
}
int n = numso.size();
// 3. 只考虑大于的部分,计算nums变成target的最小差值。
long ans = 0;
for (int i = 0; i < n; i++) {
if (to.get(i) > numso.get(i)) ans += (to.get(i) - numso.get(i)) / 2;
}
n = numsj.size();
for (int i = 0; i < n; i++) {
if (tj.get(i) > numsj.get(i)) ans += (tj.get(i) - numsj.get(i)) / 2;
}
return ans;
}
最后,本来想着使用指针进行奇和偶的划分,但是,转念一想,为啥要为难自己呢?就直接开了四个数组进行奇偶的划分了。时间 O ( l o g n ) O(logn) O(logn),空间 O ( n ) O(n) O(n),和排序的时间复杂度一致。空间是排序使用 + 自己的四个数组。
题目三:使数组相等的最小开销
数学 + 前缀和 + 二分
说实话不是特别擅长打数学公式,凑合看下吧。
下面是把数组都变成 x x x的最小开销。
s = ∑ i a b s ( x − n u m s [ i ] ) ∗ c o s t [ i ] s =\sum_iabs(x - nums[i]) * cost[i] s=∑iabs(x−nums[i])∗cost[i]
- 当x >= nums[i]的时候 = ∑ i c o s t [ i ] ∗ x − ∑ i n u m s [ i ] ∗ c o s t [ i ] \sum_icost[i] * x - \sum_inums[i]*cost[i] ∑icost[i]∗x−∑inums[i]∗cost[i]
- 当x < nums[i]的时候:=$\sum_inums[i]*cost[i]-\sum_icost[i] * x $
如果我们把(nums[i], cost[i])按照nums[i]排序,那么就可以在 O ( l o g n ) O(logn) O(logn)的时间复杂度内找到大于x和小于x的坐标i。如果已经知道了 ∑ i c o s t [ i ] \sum_icost[i] ∑icost[i]和 ∑ i n u m s [ i ] ∗ c o s t [ i ] \sum_inums[i]*cost[i] ∑inums[i]∗cost[i]就可以在 O ( 1 ) O(1) O(1)的时间复杂度内使用上面的两个公式求出s,而上述的求和在排序之后,已经成了一个连续的部分,所以可以使用前缀和进行求解。
最后看一眼数据范围1 < nums[i] < 1e6,那么可以通过遍历x的方式,因为x一定是 [ n u m s m i n v , n u m s m a x v ] [nums_{minv}, nums_{maxv}] [numsminv,numsmaxv]中的一个数字。
时间复杂度 O ( m a x ( n u m s m a x v , n ) ∗ l o g n ) O(max(nums_{maxv}, n) * logn) O(max(numsmaxv,n)∗logn),空间复杂度 O ( n ) O(n) O(n)
public long minCost(int[] nums, int[] cost) {
List<int[]> arrs = new ArrayList<>();
int n = cost.length;
for (int i = 0; i < n; i++) {
arrs.add(new int[] {nums[i], cost[i]});
}
// 按照nums进行排序
arrs.sort((x, y)->Integer.compare(x[0], y[0]));
Arrays.sort(nums);
// 求前缀和
long[] sumb = new long[n + 1];
long[] suma = new long[n + 1];
for (int i = 0; i < n; i++) {
int c = arrs.get(i)[1];
suma[i + 1] = suma[i] + c;
sumb[i + 1] = sumb[i] + (long)nums[i] * c;
}
long ans = Long.MAX_VALUE;
for (int i = nums[0]; i <= nums[n - 1]; i++) {
// 二分求解
int l = 0, r = n;
while (l < r) {
int mid = l + r >> 1;
if (nums[mid] <= i) l = mid + 1;
else r = mid;
}
long a1 = suma[l];
long a2 = suma[n] - suma[l];
long b1 = sumb[l]; // 小于,xa1 - b1
long b2 = sumb[n] - sumb[l]; // 大于,b2 - xa2
ans = Math.min(ans, (long)i * a1 - b1 + b2 - (long)i * a2);
}
return ans;
}
题目二: 最大公因数等于 K 的子数组数目
没想到简单的办法,但是数据很小,直接遍历所有子数组求符合条件就行了。
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
public int subarrayGCD(int[] nums, int k) {
// System.out.println(gcd(8, 4));
// System.out.println(gcd(8, 6));
int n = nums.length;
int ans = 0;
for (int i = 0; i < n; i++) {
int d = nums[i];
for (int j = i; j < n; j++) {
d = gcd(nums[j], d);
if (d == k) ans++;
if (d < k) break; // 小于就不可能再大了
}
}
return ans;
}
题目一: 判断两个事件是否存在冲突
- 直接字符串操作,java.date应该也可以判断,但是不知道怎么用,也不知道能不能用。
public static boolean haveConflict(String[] event1, String[] event2) {
String[] e1bg = event1[0].split(":");
String[] e1ed = event1[1].split(":");
String[] e2bg = event2[0].split(":");
String[] e2ed = event2[1].split(":");
int e1st = Integer.valueOf(e1bg[0]) * 60 + Integer.valueOf(e1bg[1]);
int e1sp = Integer.valueOf(e1ed[0]) * 60 + Integer.valueOf(e1ed[1]);
int e2st = Integer.valueOf(e2bg[0]) * 60 + Integer.valueOf(e2bg[1]);
int e2sp = Integer.valueOf(e2ed[0]) * 60 + Integer.valueOf(e2ed[1]);
if (e1st >= e2st && e1st <= e2sp) return true;
if (e2st >= e1st && e2st <= e1sp) return true;
return false;
}