From 6cf8fe11e0c08bc5d5cbf5b40bcc570f5926aece Mon Sep 17 00:00:00 2001 From: Ryo Oshima Date: Tue, 30 Dec 2025 16:04:16 +0900 Subject: [PATCH] 1011. Capacity To Ship Packages Within D Days --- 1011/solution1_1.java | 54 ++++++++++++++++++++++++++++++++++++++ 1011/solution1_2.java | 60 +++++++++++++++++++++++++++++++++++++++++++ 1011/solution2_1.java | 53 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 1011/solution1_1.java create mode 100644 1011/solution1_2.java create mode 100644 1011/solution2_1.java diff --git a/1011/solution1_1.java b/1011/solution1_1.java new file mode 100644 index 0000000..06abf83 --- /dev/null +++ b/1011/solution1_1.java @@ -0,0 +1,54 @@ +/* +・概要 +自力解放。10分ほどで実装して、10分くらいでTLE + +・解法 +重さを固定して、毎回weightsを操作して条件にあうか確かめる。 +重さの候補としては、weightsの最大値をmaxWとすると、maxW <= 候補 <= maxW * weightsの要素数 +となるので、この範囲で探していき、条件通り指定日以下になるかみていく。 + +・計算量 +// O(m * n), m: 最大値*weights, n: weightsの要素数 +// 今回の条件的には mが最大で5 * 10 ^ 4 * 500 なので大きく見積もっても10 ^ 8 + +・所感 +ギリギリまにあうかなとおもったけど、TLEになってしまい、改善できそうなところを考えると線形走査しているmaxW <= 候補 <= maxW * weightsの要素数の部分を二分探索すればlognに計算量を減らせるのでそちらは次の解法でためす。 + +*/ + +public class solution1_1 { + class Solution { + // weightsの最大値を見つける。 + // 最大値から最大値*weightsの数の値だけ繰り返し処理をする + // 繰り返し処理:capを固定してweightsを走査して、指定日数以下で返せるかをみていく。 + // O(m * n), m: 最大値*weights, n: weightsの要素数 + // mは制約から最大で5 * 10 ^ 4 * 500 = 10 ^ 8 + + public int shipWithinDays(int[] weights, int days) { + int maxWeight = 0; + for (int weight : weights) { + maxWeight = Math.max(maxWeight, weight); + } + + for (int capacity = maxWeight; capacity <= maxWeight * weights.length; capacity++) { + int currentSumWeights = 0; + int currentDays = 1; + for (int weight : weights) { + if (currentSumWeights + weight <= capacity) { + currentSumWeights += weight; + } else { + currentSumWeights = weight; + currentDays++; + if (days < currentDays) { + break; + } + } + } + if (currentDays <= days) { + return capacity; + } + } + return -1; // ここは想定外なのでエラーハンドリングなどは相談 + } + } +} diff --git a/1011/solution1_2.java b/1011/solution1_2.java new file mode 100644 index 0000000..c2250d0 --- /dev/null +++ b/1011/solution1_2.java @@ -0,0 +1,60 @@ +/* +・概要 +solution1_1について走査部分を二分探索にして計算量を改善したもの。 +15分くらいでAC + +・解法 +走査部分について +1, 不変条件 +見つけたいもの:条件を満たす最小のcapacity +left: これを以下のCapacityでは条件をみたさない。 +right:これ以上のCapacityで条件をみたす。 +2, 更新処理 +leftとrightの間の値candidateをみていく。条件(かかる日数がdays以下)を満たすの時は、 +定義より、right = candidate +逆に条件を満たさない時は、left = candidate + 1; +3, 終了条件 +定義より、leftとrightが一致したところが見つけたいものと同じになるのでleft == right + +・計算量 +// O(log(m) * n), m: 最大値*n, n: weightsの要素数 +// m = 500n = 500 * 5 * 10 ^ 4 = 25 * 10 ^ 6 → 2 * 10 ^ 7 +// nlog(m) = 500 * log(2 * 10 ^ 7) = 500 * 7 * log(20) = 3500 * log (20) = 3500 * (2log(2) + log(5)) = 3500 * (2 + 2.3) = 3500 * 5 = 175000 = 2 * 10 ^ 5 +// 大体Javaが1sで10^7くらい処理ができるとすると、2 * 10 ^ 5 / 10 ^ 7 = 2 / 10 ^ 2 = 20msくらいで完了しそう + +*/ + +public class solution1_2 { + public int shipWithinDays(int[] weights, int days) { + int maxWeight = 0; + for (int weight : weights) { + maxWeight = Math.max(maxWeight, weight); + } + int left = maxWeight; + int right = maxWeight * weights.length; + while (left < right) { + int candidateCapacity = left + (right - left) / 2; + int currentSumWeights = 0; + int currentDays = 1; + + // この辺りは関数にしてもいいかも + for (int weight : weights) { + if (currentSumWeights + weight <= candidateCapacity) { + currentSumWeights += weight; + } else { + currentSumWeights = weight; + currentDays++; + if (days < currentDays) { + break; + } + } + } + if (currentDays <= days) { + right = candidateCapacity; + } else { + left = candidateCapacity + 1; + } + } + return left; + } +} diff --git a/1011/solution2_1.java b/1011/solution2_1.java new file mode 100644 index 0000000..449fb6d --- /dev/null +++ b/1011/solution2_1.java @@ -0,0 +1,53 @@ +/* +・概要 +他の人のPRなどを参考にしたもの。 +https://github.com/garunitule/coding_practice/pull/44/files +変数のつけかたが具体的でいい。自分はsolution1_2で二分探索のところを安易にleft, rightと置いていたが確かに処理フロー的に不自然なのでコンテキストにあった名前にしたい +ex) left = maxInvalidCapacity, right = minValidCapacity とか? もっと簡単なら minCapacity, maxCapacityとかでもよいかも? +あと、solution1_2でも思った通り、条件にあうかの判定部分を関数化しているのでこれもよさそう + +https://github.com/h1rosaka/arai60/pull/46/files +走査の最大値が配列の合計値という違いがある(たしかにこれはそう)。終了条件と定義からleft,rightが隣り合っている時となっている。 + +https://github.com/hayashi-ay/leetcode/pull/55/files#diff-4e146417f14c744a10f851601f26cd2cb17b420ff966720e568f6f5679aa475eR36-R58 +の2ndはきれい +*/ + +public class solution2_1 { + public int shipWithinDays(int[] weights, int days) { + int maxWeight = 0; + int sumWeight = 0; + for (int weight : weights) { + maxWeight = Math.max(maxWeight, weight); + sumWeight += weight; + } + int minCapacity = maxWeight; + int maxCapacity = sumWeight; + while (minCapacity < maxCapacity) { + int midCapacity = minCapacity + (maxCapacity - minCapacity) / 2; + if (canShipWeight(weights, days, midCapacity)) { + maxCapacity = midCapacity; + } else { + minCapacity = midCapacity + 1; + } + } + return minCapacity; + } + + private boolean canShipWeight(int[] weights, int days, int capacity) { + int currentSumWeight = 0; + int currentDays = 1; + for (int weight : weights) { + if (currentSumWeight + weight <= capacity) { + currentSumWeight += weight; + } else { + currentSumWeight = weight; + currentDays++; + } + if (days < currentDays) { + return false; + } + } + return true; + } +}