上陸アルゴリズムI LeetCode Weekly Contest 217解題報告
No.1最も裕福な顧客の資産総量
問題を解く構想.
サブ配列和の最大値を求める.Java 8以降のstreamを使って一言で済ませることができます.
コードディスプレイ
No.2最も競争力のある都のサブシーケンスを見つけ出す
問題を解く構想.
すなわち、kの長いの辞書順が最も小さいサブシーケンスが見つかる.欲張るたびに最小の数を取ればいい.
しかし、グローバル範囲の最小数を取ることはできない、例えば、1回目の取りでは、[0,n-k]の下付き範囲で最小を取るしかない、取った最小値の下付きをxと仮定すると、2回目の取りは[x,n-k+1]の下付き範囲で最小を取るべきである.
優先キューを使用して完了することができる.
コードディスプレイ
}
No.3配列を補完する最小オペランド
問題を解く構想.
コアは依然として列挙である:列挙の最終的な和はいくらであるか.前処理のデータによって、各グループの数をこの和にするには、何個の数字を変更する必要があるかを迅速に計算する.
コードディスプレイ
No.4配列の最小オフセット
問題を解く構想.
各数には一定の変化範囲があり、例えば5は5または10にしかならず、8は1、2、4、8になることができる.
双方向の変化は扱いにくいので、まず各数をその最大値の形式にすることができます:偶数は変わらず、奇数は2を掛けることができます.
次に、最大値と最小値の差を最小にするために、いくつかの数を縮小する必要があります.
私たちが縮小する必要があるのは最大値です.最大値を縮小してこそ結果に影響を与えるからです.したがって、最大値を縮小するだけでよい.
コードディスプレイ
問題を解く構想.
サブ配列和の最大値を求める.Java 8以降のstreamを使って一言で済ませることができます.
コードディスプレイ
classSolution{
publicintmaximumWealth(int[][] accounts){
return Arrays.stream(accounts).
map(i -> Arrays.stream(i).sum()).
max(Integer::compareTo).get();
}
}
No.2最も競争力のある都のサブシーケンスを見つけ出す
問題を解く構想.
すなわち、kの長いの辞書順が最も小さいサブシーケンスが見つかる.欲張るたびに最小の数を取ればいい.
しかし、グローバル範囲の最小数を取ることはできない、例えば、1回目の取りでは、[0,n-k]の下付き範囲で最小を取るしかない、取った最小値の下付きをxと仮定すると、2回目の取りは[x,n-k+1]の下付き範囲で最小を取るべきである.
優先キューを使用して完了することができる.
コードディスプレイ
class Number {
int num;
int idx;
public Number(int num, int idx) {
this.num = num;
this.idx = idx;
}
}
public int[] mostCompetitive(int[] nums, int k) {
// ,
PriorityQueue heap = new PriorityQueue<>((a, b) -> a.num == b.num ? a.idx - b.idx : a.num - b.num);
// idx
int idx;
for (idx = 0; idx <= nums.length - k; idx++) {
heap.add(new Number(nums[idx], idx));
}
int[] res = new int[k];
int latestIdx = -1;
for (int i = 0; i < k; i++) {
Number num = heap.poll();
// latestIdx
while (num.idx < latestIdx) {
num = heap.poll();
}
// [x, n - k + i]
res[i] = num.num;
latestIdx = num.idx;
// ,
for (; idx <= nums.length - k + i + 1 && idx < nums.length; idx++) {
heap.add(new Number(nums[idx], idx));
}
}
return res;
}
}
No.3配列を補完する最小オペランド
問題を解く構想.
コアは依然として列挙である:列挙の最終的な和はいくらであるか.前処理のデータによって、各グループの数をこの和にするには、何個の数字を変更する必要があるかを迅速に計算する.
コードディスプレイ
class Solution {
public int minMoves(int[] nums, int limit) {
int halfLen = nums.length / 2;
// ,
int[] smallPart = new int[halfLen];
int[] bigPart = new int[halfLen];
for (int i = 0, j = nums.length - 1; i < j; i++, j--) {
smallPart[i] = Math.min(nums[i], nums[j]);
bigPart[i] = Math.max(nums[i], nums[j]);
}
//
int[] smallCntPreSum = new int[limit + 1];
int[] bigCntPreSum = new int[limit + 1];
for (int i = 0; i < halfLen; i++) {
smallCntPreSum[smallPart[i]]++;
bigCntPreSum[bigPart[i]]++;
}
for (int i = 1; i <= limit; i++) {
smallCntPreSum[i] += smallCntPreSum[i - 1];
bigCntPreSum[i] += bigCntPreSum[i - 1];
}
//
int[] sumCnt = new int[limit * 2 + 1];
for (int i = 0; i < halfLen; i++) {
sumCnt[smallPart[i] + bigPart[i]]++;
}
int res = nums.length;
for (int sum = 2; sum <= limit + limit; ++sum) {
// sum , tot
int tot = halfLen - sumCnt[sum];
if (sum <= limit)
tot += halfLen - smallCntPreSum[sum - 1];
else
tot += bigCntPreSum[sum - limit - 1];
res = Math.min(res, tot);
}
return res;
}
}
No.4配列の最小オフセット
問題を解く構想.
各数には一定の変化範囲があり、例えば5は5または10にしかならず、8は1、2、4、8になることができる.
双方向の変化は扱いにくいので、まず各数をその最大値の形式にすることができます:偶数は変わらず、奇数は2を掛けることができます.
次に、最大値と最小値の差を最小にするために、いくつかの数を縮小する必要があります.
私たちが縮小する必要があるのは最大値です.最大値を縮小してこそ結果に影響を与えるからです.したがって、最大値を縮小するだけでよい.
コードディスプレイ
class Solution {
public int minimumDeviation(int[] nums) {
TreeSet set = new TreeSet<>();
for (int num : nums) {
set.add(num % 2 == 0 ? num : num * 2);
}
int res = set.last() - set.first();
while (res > 0 && set.last() % 2 == 0) {
int max = set.last();
set.remove(max);
set.add(max / 2);
res = Math.min(res, set.last() - set.first());
}
return res;
}
}